From: Mieczyslaw Nalewaj Date: Fri, 27 Mar 2026 16:47:48 +0000 (+0100) Subject: kernel/generic: restore files for v6.12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=141cb99b41399d412c6e3f9e27a00ea83cf4eafd;p=thirdparty%2Fopenwrt.git kernel/generic: restore files for v6.12 This is an automatically generated commit which aids following Kernel patch history, as git will see the move and copy as a rename thus defeating the purpose. For the original discussion see: https://lists.openwrt.org/pipermail/openwrt-devel/2023-October/041673.html Signed-off-by: Mieczyslaw Nalewaj Link: https://github.com/openwrt/openwrt/pull/21078 Signed-off-by: Robert Marko --- diff --git a/target/linux/generic/backport-6.12/010-v6.19-nvmem-layouts-u-boot-env-add-optional-env-size-property.patch b/target/linux/generic/backport-6.12/010-v6.19-nvmem-layouts-u-boot-env-add-optional-env-size-property.patch new file mode 100644 index 00000000000..207161a40d0 --- /dev/null +++ b/target/linux/generic/backport-6.12/010-v6.19-nvmem-layouts-u-boot-env-add-optional-env-size-property.patch @@ -0,0 +1,62 @@ +From 06e92afca89075628b12c9b4085b4cc7320081ac Mon Sep 17 00:00:00 2001 +From: Jascha Sundaresan +Date: Thu, 23 Oct 2025 03:07:41 +0400 +Subject: nvmem: layouts: u-boot-env: add optional "env-size" property + +Some devices reserve a larger NVMEM region for the U-Boot environment +than the actual environment data length used by U-Boot itself. The CRC32 +in the U-Boot header is calculated over the smaller data length, causing +CRC validation to fail when Linux reads the full partition. + +Allow an optional device tree property "env-size" to specify the +environment data size to use for CRC computation. + +v2: add missing $ref line to DT binding + +Signed-off-by: Jascha Sundaresan +Reviewed-by: Rob Herring (Arm) +Signed-off-by: Srinivas Kandagatla +--- + Documentation/devicetree/bindings/nvmem/layouts/u-boot,env.yaml | 7 +++++++ + drivers/nvmem/layouts/u-boot-env.c | 4 +++- + 2 files changed, 10 insertions(+), 1 deletion(-) + +--- a/Documentation/devicetree/bindings/nvmem/layouts/u-boot,env.yaml ++++ b/Documentation/devicetree/bindings/nvmem/layouts/u-boot,env.yaml +@@ -46,6 +46,12 @@ properties: + type: object + description: Command to use for automatic booting + ++ env-size: ++ description: ++ Size in bytes of the environment data used by U-Boot for CRC ++ calculation. If omitted, the full NVMEM region size is used. ++ $ref: /schemas/types.yaml#/definitions/uint32 ++ + ethaddr: + type: object + description: Ethernet interfaces base MAC address. +@@ -104,6 +110,7 @@ examples: + + partition-u-boot-env { + compatible = "brcm,env"; ++ env-size = <0x20000>; + + ethaddr { + }; +--- a/drivers/nvmem/layouts/u-boot-env.c ++++ b/drivers/nvmem/layouts/u-boot-env.c +@@ -99,10 +99,12 @@ int u_boot_env_parse(struct device *dev, + uint32_t crc32; + uint32_t calc; + uint8_t *buf; ++ u32 env_size; + int bytes; + int err; + +- dev_size = nvmem_dev_size(nvmem); ++ dev_size = device_property_read_u32(dev, "env-size", &env_size) ? ++ nvmem_dev_size(nvmem) : (size_t)env_size; + + buf = kzalloc(dev_size, GFP_KERNEL); + if (!buf) { diff --git a/target/linux/generic/backport-6.12/152-v6.16-net-wireguard-add-nonstring-annotation-to-fix-build-with-GCC15.patch b/target/linux/generic/backport-6.12/152-v6.16-net-wireguard-add-nonstring-annotation-to-fix-build-with-GCC15.patch new file mode 100644 index 00000000000..1fdf2f1dbd3 --- /dev/null +++ b/target/linux/generic/backport-6.12/152-v6.16-net-wireguard-add-nonstring-annotation-to-fix-build-with-GCC15.patch @@ -0,0 +1,63 @@ +From 71e5da46e78c1cd24e2feed251a2845327447ad8 Mon Sep 17 00:00:00 2001 +From: Kees Cook +Date: Wed, 21 May 2025 23:27:04 +0200 +Subject: wireguard: global: add __nonstring annotations for unterminated + strings + +When a character array without a terminating NUL character has a static +initializer, GCC 15's -Wunterminated-string-initialization will only +warn if the array lacks the "nonstring" attribute[1]. Mark the arrays +with __nonstring to correctly identify the char array as "not a C string" +and thereby eliminate the warning: + +../drivers/net/wireguard/cookie.c:29:56: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (9 chars into 8 available) [-Wunterminated-string-initialization] + 29 | static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] = "mac1----"; + | ^~~~~~~~~~ +../drivers/net/wireguard/cookie.c:30:58: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (9 chars into 8 available) [-Wunterminated-string-initialization] + 30 | static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] = "cookie--"; + | ^~~~~~~~~~ +../drivers/net/wireguard/noise.c:28:38: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (38 chars into 37 available) [-Wunterminated-string-initialization] + 28 | static const u8 handshake_name[37] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"; + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +../drivers/net/wireguard/noise.c:29:39: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (35 chars into 34 available) [-Wunterminated-string-initialization] + 29 | static const u8 identifier_name[34] = "WireGuard v1 zx2c4 Jason@zx2c4.com"; + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The arrays are always used with their fixed size, so use __nonstring. + +Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117178 [1] +Signed-off-by: Kees Cook +Signed-off-by: Jason A. Donenfeld +Link: https://patch.msgid.link/20250521212707.1767879-3-Jason@zx2c4.com +Signed-off-by: Paolo Abeni +--- + drivers/net/wireguard/cookie.c | 4 ++-- + drivers/net/wireguard/noise.c | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireguard/cookie.c ++++ b/drivers/net/wireguard/cookie.c +@@ -26,8 +26,8 @@ void wg_cookie_checker_init(struct cooki + } + + enum { COOKIE_KEY_LABEL_LEN = 8 }; +-static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] = "mac1----"; +-static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] = "cookie--"; ++static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] __nonstring = "mac1----"; ++static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] __nonstring = "cookie--"; + + static void precompute_key(u8 key[NOISE_SYMMETRIC_KEY_LEN], + const u8 pubkey[NOISE_PUBLIC_KEY_LEN], +--- a/drivers/net/wireguard/noise.c ++++ b/drivers/net/wireguard/noise.c +@@ -25,8 +25,8 @@ + * <- e, ee, se, psk, {} + */ + +-static const u8 handshake_name[37] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"; +-static const u8 identifier_name[34] = "WireGuard v1 zx2c4 Jason@zx2c4.com"; ++static const u8 handshake_name[37] __nonstring = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"; ++static const u8 identifier_name[34] __nonstring = "WireGuard v1 zx2c4 Jason@zx2c4.com"; + static u8 handshake_init_hash[NOISE_HASH_LEN] __ro_after_init; + static u8 handshake_init_chaining_key[NOISE_HASH_LEN] __ro_after_init; + static atomic64_t keypair_counter = ATOMIC64_INIT(0); diff --git a/target/linux/generic/backport-6.12/200-01-v6.13-jiffies-Define-secs_to_jiffies.patch b/target/linux/generic/backport-6.12/200-01-v6.13-jiffies-Define-secs_to_jiffies.patch new file mode 100644 index 00000000000..ad9af1d4bb7 --- /dev/null +++ b/target/linux/generic/backport-6.12/200-01-v6.13-jiffies-Define-secs_to_jiffies.patch @@ -0,0 +1,60 @@ +From b35108a51cf7bab58d7eace1267d7965978bcdb8 Mon Sep 17 00:00:00 2001 +From: Easwar Hariharan +Date: Wed, 30 Oct 2024 17:47:35 +0000 +Subject: [PATCH] jiffies: Define secs_to_jiffies() + +secs_to_jiffies() is defined in hci_event.c and cannot be reused by +other call sites. Hoist it into the core code to allow conversion of the +~1150 usages of msecs_to_jiffies() that either: + + - use a multiplier value of 1000 or equivalently MSEC_PER_SEC, or + - have timeouts that are denominated in seconds (i.e. end in 000) + +It's implemented as a macro to allow usage in static initializers. + +This will also allow conversion of yet more sites that use (sec * HZ) +directly, and improve their readability. + +Suggested-by: Michael Kelley +Signed-off-by: Easwar Hariharan +Signed-off-by: Thomas Gleixner +Reviewed-by: Luiz Augusto von Dentz +Link: https://lore.kernel.org/all/20241030-open-coded-timeouts-v3-1-9ba123facf88@linux.microsoft.com +--- + include/linux/jiffies.h | 13 +++++++++++++ + net/bluetooth/hci_event.c | 2 -- + 2 files changed, 13 insertions(+), 2 deletions(-) + +--- a/include/linux/jiffies.h ++++ b/include/linux/jiffies.h +@@ -526,6 +526,19 @@ static __always_inline unsigned long mse + } + } + ++/** ++ * secs_to_jiffies: - convert seconds to jiffies ++ * @_secs: time in seconds ++ * ++ * Conversion is done by simple multiplication with HZ ++ * ++ * secs_to_jiffies() is defined as a macro rather than a static inline ++ * function so it can be used in static initializers. ++ * ++ * Return: jiffies value ++ */ ++#define secs_to_jiffies(_secs) ((_secs) * HZ) ++ + extern unsigned long __usecs_to_jiffies(const unsigned int u); + #if !(USEC_PER_SEC % HZ) + static inline unsigned long _usecs_to_jiffies(const unsigned int u) +--- a/net/bluetooth/hci_event.c ++++ b/net/bluetooth/hci_event.c +@@ -42,8 +42,6 @@ + #define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00\x00\x00\x00\x00\x00\x00" + +-#define secs_to_jiffies(_secs) msecs_to_jiffies((_secs) * 1000) +- + /* Handle HCI Event packets */ + + static void *hci_ev_skb_pull(struct hci_dev *hdev, struct sk_buff *skb, diff --git a/target/linux/generic/backport-6.12/200-02-v6.14-jiffies-Cast-to-unsigned-long-in-secs_to_jiffies-con.patch b/target/linux/generic/backport-6.12/200-02-v6.14-jiffies-Cast-to-unsigned-long-in-secs_to_jiffies-con.patch new file mode 100644 index 00000000000..cddd558dee7 --- /dev/null +++ b/target/linux/generic/backport-6.12/200-02-v6.14-jiffies-Cast-to-unsigned-long-in-secs_to_jiffies-con.patch @@ -0,0 +1,35 @@ +From bb2784d9ab49587ba4fbff37a319fff2924db289 Mon Sep 17 00:00:00 2001 +From: Easwar Hariharan +Date: Thu, 30 Jan 2025 19:26:58 +0000 +Subject: [PATCH] jiffies: Cast to unsigned long in secs_to_jiffies() + conversion + +While converting users of msecs_to_jiffies(), lkp reported that some range +checks would always be true because of the mismatch between the implied int +value of secs_to_jiffies() vs the unsigned long return value of the +msecs_to_jiffies() calls it was replacing. + +Fix this by casting the secs_to_jiffies() input value to unsigned long. + +Fixes: b35108a51cf7ba ("jiffies: Define secs_to_jiffies()") +Reported-by: kernel test robot +Signed-off-by: Easwar Hariharan +Signed-off-by: Thomas Gleixner +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/all/20250130192701.99626-1-eahariha@linux.microsoft.com +Closes: https://lore.kernel.org/oe-kbuild-all/202501301334.NB6NszQR-lkp@intel.com/ +--- + include/linux/jiffies.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/jiffies.h ++++ b/include/linux/jiffies.h +@@ -537,7 +537,7 @@ static __always_inline unsigned long mse + * + * Return: jiffies value + */ +-#define secs_to_jiffies(_secs) ((_secs) * HZ) ++#define secs_to_jiffies(_secs) (unsigned long)((_secs) * HZ) + + extern unsigned long __usecs_to_jiffies(const unsigned int u); + #if !(USEC_PER_SEC % HZ) 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 00000000000..5178dc83ae4 --- /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 00000000000..c02e4ed6a2f --- /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 +@@ -520,11 +520,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. +@@ -532,8 +534,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. +@@ -1041,7 +1043,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 struct clk *clk_get_sys(co + + #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/backport-6.12/203-01-v6.13-of-address-Rework-bus-matching-to-avoid-warnings.patch b/target/linux/generic/backport-6.12/203-01-v6.13-of-address-Rework-bus-matching-to-avoid-warnings.patch new file mode 100644 index 00000000000..d7a67518613 --- /dev/null +++ b/target/linux/generic/backport-6.12/203-01-v6.13-of-address-Rework-bus-matching-to-avoid-warnings.patch @@ -0,0 +1,61 @@ +From 64ee3cf096ac590e7da2ceac1c390546bff5e240 Mon Sep 17 00:00:00 2001 +From: "Rob Herring (Arm)" +Date: Fri, 8 Nov 2024 13:35:48 -0600 +Subject: [PATCH] of/address: Rework bus matching to avoid warnings + +With warnings added for deprecated #address-cells/#size-cells handling, +the DT address handling code causes warnings when used on nodes with no +address. This happens frequently with calls to of_platform_populate() as +it is perfectly acceptable to have devices without a 'reg' property. The +desired behavior is to just silently return an error when retrieving an +address. + +The warnings can be avoided by checking for "#address-cells" presence +first and checking for an address property before fetching +"#address-cells" and "#size-cells". + +Reported-by: Marek Szyprowski +Reported-by: Steven Price +Tested-by: Marek Szyprowski +Link: https://lore.kernel.org/r/20241108193547.2647986-2-robh@kernel.org +Signed-off-by: Rob Herring (Arm) +--- + drivers/of/address.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +--- a/drivers/of/address.c ++++ b/drivers/of/address.c +@@ -331,7 +331,11 @@ static unsigned int of_bus_isa_get_flags + + static int of_bus_default_flags_match(struct device_node *np) + { +- return of_bus_n_addr_cells(np) == 3; ++ /* ++ * Check for presence first since of_bus_n_addr_cells() will warn when ++ * walking parent nodes. ++ */ ++ return of_property_present(np, "#address-cells") && (of_bus_n_addr_cells(np) == 3); + } + + /* +@@ -700,16 +704,16 @@ const __be32 *__of_get_address(struct de + if (strcmp(bus->name, "pci") && (bar_no >= 0)) + return NULL; + +- bus->count_cells(dev, &na, &ns); +- if (!OF_CHECK_ADDR_COUNT(na)) +- return NULL; +- + /* Get "reg" or "assigned-addresses" property */ + prop = of_get_property(dev, bus->addresses, &psize); + if (prop == NULL) + return NULL; + psize /= 4; + ++ bus->count_cells(dev, &na, &ns); ++ if (!OF_CHECK_ADDR_COUNT(na)) ++ return NULL; ++ + onesize = na + ns; + for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) { + u32 val = be32_to_cpu(prop[0]); diff --git a/target/linux/generic/backport-6.12/203-02-v6.13-of-address-Fix-WARN-when-attempting-translating-non-.patch b/target/linux/generic/backport-6.12/203-02-v6.13-of-address-Fix-WARN-when-attempting-translating-non-.patch new file mode 100644 index 00000000000..2ad67cbde7b --- /dev/null +++ b/target/linux/generic/backport-6.12/203-02-v6.13-of-address-Fix-WARN-when-attempting-translating-non-.patch @@ -0,0 +1,85 @@ +From 6e5773d52f4a2d9c80692245f295069260cff6fc Mon Sep 17 00:00:00 2001 +From: "Rob Herring (Arm)" +Date: Fri, 10 Jan 2025 15:50:29 -0600 +Subject: [PATCH] of/address: Fix WARN when attempting translating + non-translatable addresses + +The recently added WARN() for deprecated #address-cells and #size-cells +triggered a WARN when of_platform_populate() (which calls +of_address_to_resource()) is used on nodes with non-translatable +addresses. This case is expected to return an error. + +Rework the bus matching to allow no match and make the default require +an #address-cells property. That should be safe to do as any platform +missing #address-cells would have a warning already. + +Fixes: 045b14ca5c36 ("of: WARN on deprecated #address-cells/#size-cells handling") +Tested-by: Sean Anderson +Link: https://lore.kernel.org/r/20250110215030.3637845-2-robh@kernel.org +Signed-off-by: Rob Herring (Arm) +--- + drivers/of/address.c | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +--- a/drivers/of/address.c ++++ b/drivers/of/address.c +@@ -338,6 +338,15 @@ static int of_bus_default_flags_match(st + return of_property_present(np, "#address-cells") && (of_bus_n_addr_cells(np) == 3); + } + ++static int of_bus_default_match(struct device_node *np) ++{ ++ /* ++ * Check for presence first since of_bus_n_addr_cells() will warn when ++ * walking parent nodes. ++ */ ++ return of_property_present(np, "#address-cells"); ++} ++ + /* + * Array of bus specific translators + */ +@@ -382,7 +391,7 @@ static struct of_bus of_busses[] = { + { + .name = "default", + .addresses = "reg", +- .match = NULL, ++ .match = of_bus_default_match, + .count_cells = of_bus_default_count_cells, + .map = of_bus_default_map, + .translate = of_bus_default_translate, +@@ -397,7 +406,6 @@ static struct of_bus *of_match_bus(struc + for (i = 0; i < ARRAY_SIZE(of_busses); i++) + if (!of_busses[i].match || of_busses[i].match(np)) + return &of_busses[i]; +- BUG(); + return NULL; + } + +@@ -519,6 +527,8 @@ static u64 __of_translate_address(struct + if (parent == NULL) + return OF_BAD_ADDR; + bus = of_match_bus(parent); ++ if (!bus) ++ return OF_BAD_ADDR; + + /* Count address cells & copy address locally */ + bus->count_cells(dev, &na, &ns); +@@ -562,6 +572,8 @@ static u64 __of_translate_address(struct + + /* Get new parent bus and counts */ + pbus = of_match_bus(parent); ++ if (!pbus) ++ return OF_BAD_ADDR; + pbus->count_cells(dev, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + pr_err("Bad cell count for %pOF\n", dev); +@@ -701,7 +713,7 @@ const __be32 *__of_get_address(struct de + + /* match the parent's bus type */ + bus = of_match_bus(parent); +- if (strcmp(bus->name, "pci") && (bar_no >= 0)) ++ if (!bus || (strcmp(bus->name, "pci") && (bar_no >= 0))) + return NULL; + + /* Get "reg" or "assigned-addresses" property */ diff --git a/target/linux/generic/backport-6.12/205-v6.16-of-reserved_mem-Add-functions-to-parse-memory-region.patch b/target/linux/generic/backport-6.12/205-v6.16-of-reserved_mem-Add-functions-to-parse-memory-region.patch new file mode 100644 index 00000000000..b5bbb6e723e --- /dev/null +++ b/target/linux/generic/backport-6.12/205-v6.16-of-reserved_mem-Add-functions-to-parse-memory-region.patch @@ -0,0 +1,163 @@ +From f4fcfdda2fd8834c62dcb9bfddcf1f89d190b70e Mon Sep 17 00:00:00 2001 +From: "Rob Herring (Arm)" +Date: Wed, 23 Apr 2025 14:42:13 -0500 +Subject: [PATCH] of: reserved_mem: Add functions to parse "memory-region" + +Drivers with "memory-region" properties currently have to do their own +parsing of "memory-region" properties. The result is all the drivers +have similar patterns of a call to parse "memory-region" and then get +the region's address and size. As this is a standard property, it should +have common functions for drivers to use. Add new functions to count the +number of regions and retrieve the region's address as a resource. + +Reviewed-by: Daniel Baluta +Acked-by: Arnaud Pouliquen +Link: https://lore.kernel.org/r/20250423-dt-memory-region-v2-v2-1-2fbd6ebd3c88@kernel.org +Signed-off-by: Rob Herring (Arm) +--- + drivers/of/of_reserved_mem.c | 80 +++++++++++++++++++++++++++++++++ + include/linux/of_reserved_mem.h | 26 +++++++++++ + 2 files changed, 106 insertions(+) + +--- a/drivers/of/of_reserved_mem.c ++++ b/drivers/of/of_reserved_mem.c +@@ -12,6 +12,7 @@ + #define pr_fmt(fmt) "OF: reserved mem: " fmt + + #include ++#include + #include + #include + #include +@@ -694,3 +695,82 @@ struct reserved_mem *of_reserved_mem_loo + return NULL; + } + EXPORT_SYMBOL_GPL(of_reserved_mem_lookup); ++ ++/** ++ * of_reserved_mem_region_to_resource() - Get a reserved memory region as a resource ++ * @np: node containing 'memory-region' property ++ * @idx: index of 'memory-region' property to lookup ++ * @res: Pointer to a struct resource to fill in with reserved region ++ * ++ * This function allows drivers to lookup a node's 'memory-region' property ++ * entries by index and return a struct resource for the entry. ++ * ++ * Returns 0 on success with @res filled in. Returns -ENODEV if 'memory-region' ++ * is missing or unavailable, -EINVAL for any other error. ++ */ ++int of_reserved_mem_region_to_resource(const struct device_node *np, ++ unsigned int idx, struct resource *res) ++{ ++ struct reserved_mem *rmem; ++ ++ if (!np) ++ return -EINVAL; ++ ++ struct device_node __free(device_node) *target = of_parse_phandle(np, "memory-region", idx); ++ if (!target || !of_device_is_available(target)) ++ return -ENODEV; ++ ++ rmem = of_reserved_mem_lookup(target); ++ if (!rmem) ++ return -EINVAL; ++ ++ resource_set_range(res, rmem->base, rmem->size); ++ res->name = rmem->name; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(of_reserved_mem_region_to_resource); ++ ++/** ++ * of_reserved_mem_region_to_resource_byname() - Get a reserved memory region as a resource ++ * @np: node containing 'memory-region' property ++ * @name: name of 'memory-region' property entry to lookup ++ * @res: Pointer to a struct resource to fill in with reserved region ++ * ++ * This function allows drivers to lookup a node's 'memory-region' property ++ * entries by name and return a struct resource for the entry. ++ * ++ * Returns 0 on success with @res filled in, or a negative error-code on ++ * failure. ++ */ ++int of_reserved_mem_region_to_resource_byname(const struct device_node *np, ++ const char *name, ++ struct resource *res) ++{ ++ int idx; ++ ++ if (!name) ++ return -EINVAL; ++ ++ idx = of_property_match_string(np, "memory-region-names", name); ++ if (idx < 0) ++ return idx; ++ ++ return of_reserved_mem_region_to_resource(np, idx, res); ++} ++EXPORT_SYMBOL_GPL(of_reserved_mem_region_to_resource_byname); ++ ++/** ++ * of_reserved_mem_region_count() - Return the number of 'memory-region' entries ++ * @np: node containing 'memory-region' property ++ * ++ * This function allows drivers to retrieve the number of entries for a node's ++ * 'memory-region' property. ++ * ++ * Returns the number of entries on success, or negative error code on a ++ * malformed property. ++ */ ++int of_reserved_mem_region_count(const struct device_node *np) ++{ ++ return of_count_phandle_with_args(np, "memory-region", NULL); ++} ++EXPORT_SYMBOL_GPL(of_reserved_mem_region_count); +--- a/include/linux/of_reserved_mem.h ++++ b/include/linux/of_reserved_mem.h +@@ -7,6 +7,7 @@ + + struct of_phandle_args; + struct reserved_mem_ops; ++struct resource; + + struct reserved_mem { + const char *name; +@@ -39,6 +40,12 @@ int of_reserved_mem_device_init_by_name( + void of_reserved_mem_device_release(struct device *dev); + + struct reserved_mem *of_reserved_mem_lookup(struct device_node *np); ++int of_reserved_mem_region_to_resource(const struct device_node *np, ++ unsigned int idx, struct resource *res); ++int of_reserved_mem_region_to_resource_byname(const struct device_node *np, ++ const char *name, struct resource *res); ++int of_reserved_mem_region_count(const struct device_node *np); ++ + #else + + #define RESERVEDMEM_OF_DECLARE(name, compat, init) \ +@@ -63,6 +70,25 @@ static inline struct reserved_mem *of_re + { + return NULL; + } ++ ++static inline int of_reserved_mem_region_to_resource(const struct device_node *np, ++ unsigned int idx, ++ struct resource *res) ++{ ++ return -ENOSYS; ++} ++ ++static inline int of_reserved_mem_region_to_resource_byname(const struct device_node *np, ++ const char *name, ++ struct resource *res) ++{ ++ return -ENOSYS; ++} ++ ++static inline int of_reserved_mem_region_count(const struct device_node *np) ++{ ++ return 0; ++} + #endif + + /** diff --git a/target/linux/generic/backport-6.12/210-v6.13-fortify-Hide-run-time-copy-size-from-value-range-tracking.patch b/target/linux/generic/backport-6.12/210-v6.13-fortify-Hide-run-time-copy-size-from-value-range-tracking.patch new file mode 100644 index 00000000000..8dca6498667 --- /dev/null +++ b/target/linux/generic/backport-6.12/210-v6.13-fortify-Hide-run-time-copy-size-from-value-range-tracking.patch @@ -0,0 +1,155 @@ +From 239d87327dcd361b0098038995f8908f3296864f Mon Sep 17 00:00:00 2001 +From: Kees Cook +Date: Thu, 12 Dec 2024 17:28:06 -0800 +Subject: fortify: Hide run-time copy size from value range tracking +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +GCC performs value range tracking for variables as a way to provide better +diagnostics. One place this is regularly seen is with warnings associated +with bounds-checking, e.g. -Wstringop-overflow, -Wstringop-overread, +-Warray-bounds, etc. In order to keep the signal-to-noise ratio high, +warnings aren't emitted when a value range spans the entire value range +representable by a given variable. For example: + + unsigned int len; + char dst[8]; + ... + memcpy(dst, src, len); + +If len's value is unknown, it has the full "unsigned int" range of [0, +UINT_MAX], and GCC's compile-time bounds checks against memcpy() will +be ignored. However, when a code path has been able to narrow the range: + + if (len > 16) + return; + memcpy(dst, src, len); + +Then the range will be updated for the execution path. Above, len is +now [0, 16] when reading memcpy(), so depending on other optimizations, +we might see a -Wstringop-overflow warning like: + + error: '__builtin_memcpy' writing between 9 and 16 bytes into region of size 8 [-Werror=stringop-overflow] + +When building with CONFIG_FORTIFY_SOURCE, the fortified run-time bounds +checking can appear to narrow value ranges of lengths for memcpy(), +depending on how the compiler constructs the execution paths during +optimization passes, due to the checks against the field sizes. For +example: + + if (p_size_field != SIZE_MAX && + p_size != p_size_field && p_size_field < size) + +As intentionally designed, these checks only affect the kernel warnings +emitted at run-time and do not block the potentially overflowing memcpy(), +so GCC thinks it needs to produce a warning about the resulting value +range that might be reaching the memcpy(). + +We have seen this manifest a few times now, with the most recent being +with cpumasks: + +In function ‘bitmap_copy’, + inlined from ‘cpumask_copy’ at ./include/linux/cpumask.h:839:2, + inlined from ‘__padata_set_cpumasks’ at kernel/padata.c:730:2: +./include/linux/fortify-string.h:114:33: error: ‘__builtin_memcpy’ reading between 257 and 536870904 bytes from a region of size 256 [-Werror=stringop-overread] + 114 | #define __underlying_memcpy __builtin_memcpy + | ^ +./include/linux/fortify-string.h:633:9: note: in expansion of macro ‘__underlying_memcpy’ + 633 | __underlying_##op(p, q, __fortify_size); \ + | ^~~~~~~~~~~~~ +./include/linux/fortify-string.h:678:26: note: in expansion of macro ‘__fortify_memcpy_chk’ + 678 | #define memcpy(p, q, s) __fortify_memcpy_chk(p, q, s, \ + | ^~~~~~~~~~~~~~~~~~~~ +./include/linux/bitmap.h:259:17: note: in expansion of macro ‘memcpy’ + 259 | memcpy(dst, src, len); + | ^~~~~~ +kernel/padata.c: In function ‘__padata_set_cpumasks’: +kernel/padata.c:713:48: note: source object ‘pcpumask’ of size [0, 256] + 713 | cpumask_var_t pcpumask, + | ~~~~~~~~~~~~~~^~~~~~~~ + +This warning is _not_ emitted when CONFIG_FORTIFY_SOURCE is disabled, +and with the recent -fdiagnostics-details we can confirm the origin of +the warning is due to FORTIFY's bounds checking: + +../include/linux/bitmap.h:259:17: note: in expansion of macro 'memcpy' + 259 | memcpy(dst, src, len); + | ^~~~~~ + '__padata_set_cpumasks': events 1-2 +../include/linux/fortify-string.h:613:36: + 612 | if (p_size_field != SIZE_MAX && + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 613 | p_size != p_size_field && p_size_field < size) + | ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~ + | | + | (1) when the condition is evaluated to false + | (2) when the condition is evaluated to true + '__padata_set_cpumasks': event 3 + 114 | #define __underlying_memcpy __builtin_memcpy + | ^ + | | + | (3) out of array bounds here + +Note that the cpumask warning started appearing since bitmap functions +were recently marked __always_inline in commit ed8cd2b3bd9f ("bitmap: +Switch from inline to __always_inline"), which allowed GCC to gain +visibility into the variables as they passed through the FORTIFY +implementation. + +In order to silence these false positives but keep otherwise deterministic +compile-time warnings intact, hide the length variable from GCC with +OPTIMIZE_HIDE_VAR() before calling the builtin memcpy. + +Additionally add a comment about why all the macro args have copies with +const storage. + +Reported-by: "Thomas Weißschuh" +Closes: https://lore.kernel.org/all/db7190c8-d17f-4a0d-bc2f-5903c79f36c2@t-8ch.de/ +Reported-by: Nilay Shroff +Closes: https://lore.kernel.org/all/20241112124127.1666300-1-nilay@linux.ibm.com/ +Tested-by: Nilay Shroff +Acked-by: Yury Norov +Acked-by: Greg Kroah-Hartman +Signed-off-by: Kees Cook +--- + include/linux/fortify-string.h | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +--- a/include/linux/fortify-string.h ++++ b/include/linux/fortify-string.h +@@ -616,6 +616,12 @@ __FORTIFY_INLINE bool fortify_memcpy_chk + return false; + } + ++/* ++ * To work around what seems to be an optimizer bug, the macro arguments ++ * need to have const copies or the values end up changed by the time they ++ * reach fortify_warn_once(). See commit 6f7630b1b5bc ("fortify: Capture ++ * __bos() results in const temp vars") for more details. ++ */ + #define __fortify_memcpy_chk(p, q, size, p_size, q_size, \ + p_size_field, q_size_field, op) ({ \ + const size_t __fortify_size = (size_t)(size); \ +@@ -623,6 +629,8 @@ __FORTIFY_INLINE bool fortify_memcpy_chk + const size_t __q_size = (q_size); \ + const size_t __p_size_field = (p_size_field); \ + const size_t __q_size_field = (q_size_field); \ ++ /* Keep a mutable version of the size for the final copy. */ \ ++ size_t __copy_size = __fortify_size; \ + fortify_warn_once(fortify_memcpy_chk(__fortify_size, __p_size, \ + __q_size, __p_size_field, \ + __q_size_field, FORTIFY_FUNC_ ##op), \ +@@ -630,7 +638,11 @@ __FORTIFY_INLINE bool fortify_memcpy_chk + __fortify_size, \ + "field \"" #p "\" at " FILE_LINE, \ + __p_size_field); \ +- __underlying_##op(p, q, __fortify_size); \ ++ /* Hide only the run-time size from value range tracking to */ \ ++ /* silence compile-time false positive bounds warnings. */ \ ++ if (!__builtin_constant_p(__copy_size)) \ ++ OPTIMIZER_HIDE_VAR(__copy_size); \ ++ __underlying_##op(p, q, __copy_size); \ + }) + + /* diff --git a/target/linux/generic/backport-6.12/211-01-v6.19-bitfield-Add-less-checking-__FIELD_-GET-PREP.patch b/target/linux/generic/backport-6.12/211-01-v6.19-bitfield-Add-less-checking-__FIELD_-GET-PREP.patch new file mode 100644 index 00000000000..1c5654237fa --- /dev/null +++ b/target/linux/generic/backport-6.12/211-01-v6.19-bitfield-Add-less-checking-__FIELD_-GET-PREP.patch @@ -0,0 +1,103 @@ +From 2a6c045640c38a407a39cd40c3c4d8dd2fd89aa8 Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Thu, 6 Nov 2025 14:34:00 +0100 +Subject: [PATCH 1/2] bitfield: Add less-checking __FIELD_{GET,PREP}() + +The BUILD_BUG_ON_MSG() check against "~0ull" works only with "unsigned +(long) long" _mask types. For constant masks, that condition is usually +met, as GENMASK() yields an UL value. The few places where the +constant mask is stored in an intermediate variable were fixed by +changing the variable type to u64 (see e.g. [1] and [2]). + +However, for non-constant masks, smaller unsigned types should be valid, +too, but currently lead to "result of comparison of constant +18446744073709551615 with expression of type ... is always +false"-warnings with clang and W=1. + +Hence refactor the __BF_FIELD_CHECK() helper, and factor out +__FIELD_{GET,PREP}(). The later lack the single problematic check, but +are otherwise identical to FIELD_{GET,PREP}(), and are intended to be +used in the fully non-const variants later. + +[1] commit 5c667d5a5a3ec166 ("clk: sp7021: Adjust width of _m in + HWM_FIELD_PREP()") +[2] commit cfd6fb45cfaf46fa ("crypto: ccree - avoid out-of-range + warnings from clang") + +Signed-off-by: Geert Uytterhoeven +Link: https://git.kernel.org/torvalds/c/5c667d5a5a3ec166 [1] +Signed-off-by: Yury Norov (NVIDIA) +--- + include/linux/bitfield.h | 36 ++++++++++++++++++++++++++++-------- + 1 file changed, 28 insertions(+), 8 deletions(-) + +--- a/include/linux/bitfield.h ++++ b/include/linux/bitfield.h +@@ -60,7 +60,7 @@ + + #define __bf_cast_unsigned(type, x) ((__unsigned_scalar_typeof(type))(x)) + +-#define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx) \ ++#define __BF_FIELD_CHECK_MASK(_mask, _val, _pfx) \ + ({ \ + BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask), \ + _pfx "mask is not constant"); \ +@@ -69,13 +69,33 @@ + ~((_mask) >> __bf_shf(_mask)) & \ + (0 + (_val)) : 0, \ + _pfx "value too large for the field"); \ +- BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \ +- __bf_cast_unsigned(_reg, ~0ull), \ +- _pfx "type of reg too small for mask"); \ + __BUILD_BUG_ON_NOT_POWER_OF_2((_mask) + \ + (1ULL << __bf_shf(_mask))); \ + }) + ++#define __BF_FIELD_CHECK_REG(mask, reg, pfx) \ ++ BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \ ++ __bf_cast_unsigned(reg, ~0ull), \ ++ pfx "type of reg too small for mask") ++ ++#define __BF_FIELD_CHECK(mask, reg, val, pfx) \ ++ ({ \ ++ __BF_FIELD_CHECK_MASK(mask, val, pfx); \ ++ __BF_FIELD_CHECK_REG(mask, reg, pfx); \ ++ }) ++ ++#define __FIELD_PREP(mask, val, pfx) \ ++ ({ \ ++ __BF_FIELD_CHECK_MASK(mask, val, pfx); \ ++ ((typeof(mask))(val) << __bf_shf(mask)) & (mask); \ ++ }) ++ ++#define __FIELD_GET(mask, reg, pfx) \ ++ ({ \ ++ __BF_FIELD_CHECK_MASK(mask, 0U, pfx); \ ++ (typeof(mask))(((reg) & (mask)) >> __bf_shf(mask)); \ ++ }) ++ + /** + * FIELD_MAX() - produce the maximum value representable by a field + * @_mask: shifted mask defining the field's length and position +@@ -112,8 +132,8 @@ + */ + #define FIELD_PREP(_mask, _val) \ + ({ \ +- __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \ +- ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ ++ __BF_FIELD_CHECK_REG(_mask, 0ULL, "FIELD_PREP: "); \ ++ __FIELD_PREP(_mask, _val, "FIELD_PREP: "); \ + }) + + #define __BF_CHECK_POW2(n) BUILD_BUG_ON_ZERO(((n) & ((n) - 1)) != 0) +@@ -152,8 +172,8 @@ + */ + #define FIELD_GET(_mask, _reg) \ + ({ \ +- __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: "); \ +- (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ ++ __BF_FIELD_CHECK_REG(_mask, _reg, "FIELD_GET: "); \ ++ __FIELD_GET(_mask, _reg, "FIELD_GET: "); \ + }) + + extern void __compiletime_error("value doesn't fit into mask") diff --git a/target/linux/generic/backport-6.12/211-02-v6.19-bitfield-Add-non-constant-field_-prep-get-helpers.patch b/target/linux/generic/backport-6.12/211-02-v6.19-bitfield-Add-non-constant-field_-prep-get-helpers.patch new file mode 100644 index 00000000000..4257ec839b5 --- /dev/null +++ b/target/linux/generic/backport-6.12/211-02-v6.19-bitfield-Add-non-constant-field_-prep-get-helpers.patch @@ -0,0 +1,114 @@ +From c1c6ab80b25c8db1e2ef5ae3ac8075d2c242ae13 Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Thu, 6 Nov 2025 14:34:01 +0100 +Subject: [PATCH 2/2] bitfield: Add non-constant field_{prep,get}() helpers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The existing FIELD_{GET,PREP}() macros are limited to compile-time +constants. However, it is very common to prepare or extract bitfield +elements where the bitfield mask is not a compile-time constant. + +To avoid this limitation, the AT91 clock driver and several other +drivers already have their own non-const field_{prep,get}() macros. +Make them available for general use by adding them to +, and improve them slightly: + 1. Avoid evaluating macro parameters more than once, + 2. Replace "ffs() - 1" by "__ffs()", + 3. Support 64-bit use on 32-bit architectures, + 4. Wire field_{get,prep}() to FIELD_{GET,PREP}() when mask is + actually constant. + +This is deliberately not merged into the existing FIELD_{GET,PREP}() +macros, as people expressed the desire to keep stricter variants for +increased safety, or for performance critical paths. + +Yury: use __mask withing new macros. + +Signed-off-by: Geert Uytterhoeven +Acked-by: Alexandre Belloni +Acked-by: Jonathan Cameron +Acked-by: Crt Mori +Acked-by: Nuno Sá +Acked-by: Richard Genoud +Reviewed-by: Andy Shevchenko +Reviewed-by: Yury Norov (NVIDIA) +Signed-off-by: Yury Norov (NVIDIA) +--- + include/linux/bitfield.h | 59 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 59 insertions(+) + +--- a/include/linux/bitfield.h ++++ b/include/linux/bitfield.h +@@ -16,6 +16,7 @@ + * FIELD_{GET,PREP} macros take as first parameter shifted mask + * from which they extract the base mask and shift amount. + * Mask must be a compilation time constant. ++ * field_{get,prep} are variants that take a non-const mask. + * + * Example: + * +@@ -223,4 +224,62 @@ __MAKE_OP(64) + #undef __MAKE_OP + #undef ____MAKE_OP + ++#define __field_prep(mask, val) \ ++ ({ \ ++ __auto_type __mask = (mask); \ ++ typeof(__mask) __val = (val); \ ++ unsigned int __shift = BITS_PER_TYPE(__mask) <= 32 ? \ ++ __ffs(__mask) : __ffs64(__mask); \ ++ (__val << __shift) & __mask; \ ++ }) ++ ++#define __field_get(mask, reg) \ ++ ({ \ ++ __auto_type __mask = (mask); \ ++ typeof(__mask) __reg = (reg); \ ++ unsigned int __shift = BITS_PER_TYPE(__mask) <= 32 ? \ ++ __ffs(__mask) : __ffs64(__mask); \ ++ (__reg & __mask) >> __shift; \ ++ }) ++ ++/** ++ * field_prep() - prepare a bitfield element ++ * @mask: shifted mask defining the field's length and position, must be ++ * non-zero ++ * @val: value to put in the field ++ * ++ * Return: field value masked and shifted to its final destination ++ * ++ * field_prep() masks and shifts up the value. The result should be ++ * combined with other fields of the bitfield using logical OR. ++ * Unlike FIELD_PREP(), @mask is not limited to a compile-time constant. ++ * Typical usage patterns are a value stored in a table, or calculated by ++ * shifting a constant by a variable number of bits. ++ * If you want to ensure that @mask is a compile-time constant, please use ++ * FIELD_PREP() directly instead. ++ */ ++#define field_prep(mask, val) \ ++ (__builtin_constant_p(mask) ? __FIELD_PREP(mask, val, "field_prep: ") \ ++ : __field_prep(mask, val)) ++ ++/** ++ * field_get() - extract a bitfield element ++ * @mask: shifted mask defining the field's length and position, must be ++ * non-zero ++ * @reg: value of entire bitfield ++ * ++ * Return: extracted field value ++ * ++ * field_get() extracts the field specified by @mask from the ++ * bitfield passed in as @reg by masking and shifting it down. ++ * Unlike FIELD_GET(), @mask is not limited to a compile-time constant. ++ * Typical usage patterns are a value stored in a table, or calculated by ++ * shifting a constant by a variable number of bits. ++ * If you want to ensure that @mask is a compile-time constant, please use ++ * FIELD_GET() directly instead. ++ */ ++#define field_get(mask, reg) \ ++ (__builtin_constant_p(mask) ? __FIELD_GET(mask, reg, "field_get: ") \ ++ : __field_get(mask, reg)) ++ + #endif diff --git a/target/linux/generic/backport-6.12/212-v6.17-of-reserved_mem-Add-missing-IORESOURCE_MEM-flag-on-r.patch b/target/linux/generic/backport-6.12/212-v6.17-of-reserved_mem-Add-missing-IORESOURCE_MEM-flag-on-r.patch new file mode 100644 index 00000000000..1f2d52af9ac --- /dev/null +++ b/target/linux/generic/backport-6.12/212-v6.17-of-reserved_mem-Add-missing-IORESOURCE_MEM-flag-on-r.patch @@ -0,0 +1,32 @@ +From aea70964b5a7ca491a3701f2dde6c9d05d51878d Mon Sep 17 00:00:00 2001 +From: "Rob Herring (Arm)" +Date: Wed, 20 Aug 2025 14:28:04 -0500 +Subject: of: reserved_mem: Add missing IORESOURCE_MEM flag on resources + +Commit f4fcfdda2fd8 ('of: reserved_mem: Add functions to parse +"memory-region"') failed to set IORESOURCE_MEM flag on the resources. +The result is functions such as devm_ioremap_resource_wc() will fail. +Add the missing flag. + +Fixes: f4fcfdda2fd8 ('of: reserved_mem: Add functions to parse "memory-region"') +Reported-by: Iuliana Prodan +Reported-by: Daniel Baluta +Tested-by: Iuliana Prodan +Reviewed-by: Iuliana Prodan +Reviewed-by: Saravana Kannan +Link: https://lore.kernel.org/r/20250820192805.565568-1-robh@kernel.org +Signed-off-by: Rob Herring (Arm) +--- + drivers/of/of_reserved_mem.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/of/of_reserved_mem.c ++++ b/drivers/of/of_reserved_mem.c +@@ -725,6 +725,7 @@ int of_reserved_mem_region_to_resource(c + return -EINVAL; + + resource_set_range(res, rmem->base, rmem->size); ++ res->flags = IORESOURCE_MEM; + res->name = rmem->name; + return 0; + } diff --git a/target/linux/generic/backport-6.12/400-v6.14-mtd-spinand-add-support-for-FORESEE-F35SQA001G.patch b/target/linux/generic/backport-6.12/400-v6.14-mtd-spinand-add-support-for-FORESEE-F35SQA001G.patch new file mode 100644 index 00000000000..84b3b2afee5 --- /dev/null +++ b/target/linux/generic/backport-6.12/400-v6.14-mtd-spinand-add-support-for-FORESEE-F35SQA001G.patch @@ -0,0 +1,38 @@ +From ae461cde5c559675fc4c0ba351c7c31ace705f56 Mon Sep 17 00:00:00 2001 +From: Bohdan Chubuk +Date: Sun, 10 Nov 2024 22:50:47 +0200 +Subject: [PATCH] mtd: spinand: add support for FORESEE F35SQA001G + +Add support for FORESEE F35SQA001G SPI NAND. + +Similar to F35SQA002G, but differs in capacity. +Datasheet: + - https://cdn.ozdisan.com/ETicaret_Dosya/704795_871495.pdf + +Tested on Xiaomi AX3000T flashed with OpenWRT. + +Signed-off-by: Bohdan Chubuk +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/spi/foresee.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/mtd/nand/spi/foresee.c ++++ b/drivers/mtd/nand/spi/foresee.c +@@ -81,6 +81,16 @@ static const struct spinand_info foresee + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&f35sqa002g_ooblayout, + f35sqa002g_ecc_get_status)), ++ SPINAND_INFO("F35SQA001G", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x71, 0x71), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(1, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&f35sqa002g_ooblayout, ++ f35sqa002g_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops foresee_spinand_manuf_ops = { diff --git a/target/linux/generic/backport-6.12/401-v6.17-mtd-spinand-add-support-for-FudanMicro-FM25S01A.patch b/target/linux/generic/backport-6.12/401-v6.17-mtd-spinand-add-support-for-FudanMicro-FM25S01A.patch new file mode 100644 index 00000000000..0199fcbde95 --- /dev/null +++ b/target/linux/generic/backport-6.12/401-v6.17-mtd-spinand-add-support-for-FudanMicro-FM25S01A.patch @@ -0,0 +1,124 @@ +From 5f284dc15ca8695d0394414045ac64616a3b0e69 Mon Sep 17 00:00:00 2001 +From: Tianling Shen +Date: Mon, 25 Aug 2025 01:00:13 +0800 +Subject: [PATCH] mtd: spinand: add support for FudanMicro FM25S01A + +Add support for FudanMicro FM25S01A SPI NAND. +Datasheet: http://eng.fmsh.com/nvm/FM25S01A_ds_eng.pdf + +Signed-off-by: Tianling Shen +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20250824170013.3328777-1-cnsztl@gmail.com +--- + drivers/mtd/nand/spi/Makefile | 2 +- + drivers/mtd/nand/spi/core.c | 1 + + drivers/mtd/nand/spi/fmsh.c | 74 +++++++++++++++++++++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 4 files changed, 77 insertions(+), 1 deletion(-) + create mode 100644 drivers/mtd/nand/spi/fmsh.c + +--- a/drivers/mtd/nand/spi/Makefile ++++ b/drivers/mtd/nand/spi/Makefile +@@ -1,4 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0 +-spinand-objs := core.o alliancememory.o ato.o esmt.o foresee.o gigadevice.o macronix.o ++spinand-objs := core.o alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o macronix.o + spinand-objs += micron.o paragon.o toshiba.o winbond.o xtx.o + obj-$(CONFIG_MTD_SPI_NAND) += spinand.o +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -1123,6 +1123,7 @@ static const struct spinand_manufacturer + &alliancememory_spinand_manufacturer, + &ato_spinand_manufacturer, + &esmt_c8_spinand_manufacturer, ++ &fmsh_spinand_manufacturer, + &foresee_spinand_manufacturer, + &gigadevice_spinand_manufacturer, + ¯onix_spinand_manufacturer, +--- /dev/null ++++ b/drivers/mtd/nand/spi/fmsh.c +@@ -0,0 +1,74 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd. ++ * ++ * Author: Dingqiang Lin ++ */ ++ ++#include ++#include ++#include ++ ++#define SPINAND_MFR_FMSH 0xA1 ++ ++static SPINAND_OP_VARIANTS(read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(write_cache_variants, ++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_variants, ++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ ++static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ return -ERANGE; ++} ++ ++static int fm25s01a_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section) ++ return -ERANGE; ++ ++ region->offset = 2; ++ region->length = 62; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops fm25s01a_ooblayout = { ++ .ecc = fm25s01a_ooblayout_ecc, ++ .free = fm25s01a_ooblayout_free, ++}; ++ ++static const struct spinand_info fmsh_spinand_table[] = { ++ SPINAND_INFO("FM25S01A", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(1, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)), ++}; ++ ++static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = { ++}; ++ ++const struct spinand_manufacturer fmsh_spinand_manufacturer = { ++ .id = SPINAND_MFR_FMSH, ++ .name = "Fudan Micro", ++ .chips = fmsh_spinand_table, ++ .nchips = ARRAY_SIZE(fmsh_spinand_table), ++ .ops = &fmsh_spinand_manuf_ops, ++}; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -263,6 +263,7 @@ struct spinand_manufacturer { + extern const struct spinand_manufacturer alliancememory_spinand_manufacturer; + extern const struct spinand_manufacturer ato_spinand_manufacturer; + extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; ++extern const struct spinand_manufacturer fmsh_spinand_manufacturer; + extern const struct spinand_manufacturer foresee_spinand_manufacturer; + extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; + extern const struct spinand_manufacturer macronix_spinand_manufacturer; diff --git a/target/linux/generic/backport-6.12/402-v6.18-mtd-nand-move-chunk-to-core.patch b/target/linux/generic/backport-6.12/402-v6.18-mtd-nand-move-chunk-to-core.patch new file mode 100644 index 00000000000..7933874d014 --- /dev/null +++ b/target/linux/generic/backport-6.12/402-v6.18-mtd-nand-move-chunk-to-core.patch @@ -0,0 +1,329 @@ +From 6b88293aae7fb78872e5cc1ec36e2f750ae12e38 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Wed, 10 Sep 2025 14:32:58 -0400 +Subject: mtd: nand: move nand_check_erased_ecc_chunk() to nand/core + +The check function for bitflips in erased blocks will be needed +by the Realtek ECC engine driver (which is currently under +development). Right now it is located in raw/nand_base.c. +While this is sufficient for the current usecases, there is +no real dependency for an ECC engine on the raw nand library. + +Move the function over to a more generic place in core library. + +Suggested-by: Miquel Raynal +Signed-off-by: Markus Stockhausen +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/core.c | 131 +++++++++++++++++++++++++++++++++++++++ + drivers/mtd/nand/raw/nand_base.c | 131 --------------------------------------- + include/linux/mtd/nand.h | 5 ++ + include/linux/mtd/rawnand.h | 5 -- + 4 files changed, 136 insertions(+), 136 deletions(-) + +--- a/drivers/mtd/nand/core.c ++++ b/drivers/mtd/nand/core.c +@@ -13,6 +13,137 @@ + #include + + /** ++ * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data ++ * @buf: buffer to test ++ * @len: buffer length ++ * @bitflips_threshold: maximum number of bitflips ++ * ++ * Check if a buffer contains only 0xff, which means the underlying region ++ * has been erased and is ready to be programmed. ++ * The bitflips_threshold specify the maximum number of bitflips before ++ * considering the region is not erased. ++ * Note: The logic of this function has been extracted from the memweight ++ * implementation, except that nand_check_erased_buf function exit before ++ * testing the whole buffer if the number of bitflips exceed the ++ * bitflips_threshold value. ++ * ++ * Returns a positive number of bitflips less than or equal to ++ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the ++ * threshold. ++ */ ++static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold) ++{ ++ const unsigned char *bitmap = buf; ++ int bitflips = 0; ++ int weight; ++ ++ for (; len && ((uintptr_t)bitmap) % sizeof(long); ++ len--, bitmap++) { ++ weight = hweight8(*bitmap); ++ bitflips += BITS_PER_BYTE - weight; ++ if (unlikely(bitflips > bitflips_threshold)) ++ return -EBADMSG; ++ } ++ ++ for (; len >= sizeof(long); ++ len -= sizeof(long), bitmap += sizeof(long)) { ++ unsigned long d = *((unsigned long *)bitmap); ++ if (d == ~0UL) ++ continue; ++ weight = hweight_long(d); ++ bitflips += BITS_PER_LONG - weight; ++ if (unlikely(bitflips > bitflips_threshold)) ++ return -EBADMSG; ++ } ++ ++ for (; len > 0; len--, bitmap++) { ++ weight = hweight8(*bitmap); ++ bitflips += BITS_PER_BYTE - weight; ++ if (unlikely(bitflips > bitflips_threshold)) ++ return -EBADMSG; ++ } ++ ++ return bitflips; ++} ++ ++/** ++ * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only ++ * 0xff data ++ * @data: data buffer to test ++ * @datalen: data length ++ * @ecc: ECC buffer ++ * @ecclen: ECC length ++ * @extraoob: extra OOB buffer ++ * @extraooblen: extra OOB length ++ * @bitflips_threshold: maximum number of bitflips ++ * ++ * Check if a data buffer and its associated ECC and OOB data contains only ++ * 0xff pattern, which means the underlying region has been erased and is ++ * ready to be programmed. ++ * The bitflips_threshold specify the maximum number of bitflips before ++ * considering the region as not erased. ++ * ++ * Note: ++ * 1/ ECC algorithms are working on pre-defined block sizes which are usually ++ * different from the NAND page size. When fixing bitflips, ECC engines will ++ * report the number of errors per chunk, and the NAND core infrastructure ++ * expect you to return the maximum number of bitflips for the whole page. ++ * This is why you should always use this function on a single chunk and ++ * not on the whole page. After checking each chunk you should update your ++ * max_bitflips value accordingly. ++ * 2/ When checking for bitflips in erased pages you should not only check ++ * the payload data but also their associated ECC data, because a user might ++ * have programmed almost all bits to 1 but a few. In this case, we ++ * shouldn't consider the chunk as erased, and checking ECC bytes prevent ++ * this case. ++ * 3/ The extraoob argument is optional, and should be used if some of your OOB ++ * data are protected by the ECC engine. ++ * It could also be used if you support subpages and want to attach some ++ * extra OOB data to an ECC chunk. ++ * ++ * Returns a positive number of bitflips less than or equal to ++ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the ++ * threshold. In case of success, the passed buffers are filled with 0xff. ++ */ ++int nand_check_erased_ecc_chunk(void *data, int datalen, ++ void *ecc, int ecclen, ++ void *extraoob, int extraooblen, ++ int bitflips_threshold) ++{ ++ int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0; ++ ++ data_bitflips = nand_check_erased_buf(data, datalen, ++ bitflips_threshold); ++ if (data_bitflips < 0) ++ return data_bitflips; ++ ++ bitflips_threshold -= data_bitflips; ++ ++ ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold); ++ if (ecc_bitflips < 0) ++ return ecc_bitflips; ++ ++ bitflips_threshold -= ecc_bitflips; ++ ++ extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen, ++ bitflips_threshold); ++ if (extraoob_bitflips < 0) ++ return extraoob_bitflips; ++ ++ if (data_bitflips) ++ memset(data, 0xff, datalen); ++ ++ if (ecc_bitflips) ++ memset(ecc, 0xff, ecclen); ++ ++ if (extraoob_bitflips) ++ memset(extraoob, 0xff, extraooblen); ++ ++ return data_bitflips + ecc_bitflips + extraoob_bitflips; ++} ++EXPORT_SYMBOL(nand_check_erased_ecc_chunk); ++ ++/** + * nanddev_isbad() - Check if a block is bad + * @nand: NAND device + * @pos: position pointing to the block we want to check +--- a/drivers/mtd/nand/raw/nand_base.c ++++ b/drivers/mtd/nand/raw/nand_base.c +@@ -2784,137 +2784,6 @@ int nand_set_features(struct nand_chip * + } + + /** +- * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data +- * @buf: buffer to test +- * @len: buffer length +- * @bitflips_threshold: maximum number of bitflips +- * +- * Check if a buffer contains only 0xff, which means the underlying region +- * has been erased and is ready to be programmed. +- * The bitflips_threshold specify the maximum number of bitflips before +- * considering the region is not erased. +- * Note: The logic of this function has been extracted from the memweight +- * implementation, except that nand_check_erased_buf function exit before +- * testing the whole buffer if the number of bitflips exceed the +- * bitflips_threshold value. +- * +- * Returns a positive number of bitflips less than or equal to +- * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the +- * threshold. +- */ +-static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold) +-{ +- const unsigned char *bitmap = buf; +- int bitflips = 0; +- int weight; +- +- for (; len && ((uintptr_t)bitmap) % sizeof(long); +- len--, bitmap++) { +- weight = hweight8(*bitmap); +- bitflips += BITS_PER_BYTE - weight; +- if (unlikely(bitflips > bitflips_threshold)) +- return -EBADMSG; +- } +- +- for (; len >= sizeof(long); +- len -= sizeof(long), bitmap += sizeof(long)) { +- unsigned long d = *((unsigned long *)bitmap); +- if (d == ~0UL) +- continue; +- weight = hweight_long(d); +- bitflips += BITS_PER_LONG - weight; +- if (unlikely(bitflips > bitflips_threshold)) +- return -EBADMSG; +- } +- +- for (; len > 0; len--, bitmap++) { +- weight = hweight8(*bitmap); +- bitflips += BITS_PER_BYTE - weight; +- if (unlikely(bitflips > bitflips_threshold)) +- return -EBADMSG; +- } +- +- return bitflips; +-} +- +-/** +- * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only +- * 0xff data +- * @data: data buffer to test +- * @datalen: data length +- * @ecc: ECC buffer +- * @ecclen: ECC length +- * @extraoob: extra OOB buffer +- * @extraooblen: extra OOB length +- * @bitflips_threshold: maximum number of bitflips +- * +- * Check if a data buffer and its associated ECC and OOB data contains only +- * 0xff pattern, which means the underlying region has been erased and is +- * ready to be programmed. +- * The bitflips_threshold specify the maximum number of bitflips before +- * considering the region as not erased. +- * +- * Note: +- * 1/ ECC algorithms are working on pre-defined block sizes which are usually +- * different from the NAND page size. When fixing bitflips, ECC engines will +- * report the number of errors per chunk, and the NAND core infrastructure +- * expect you to return the maximum number of bitflips for the whole page. +- * This is why you should always use this function on a single chunk and +- * not on the whole page. After checking each chunk you should update your +- * max_bitflips value accordingly. +- * 2/ When checking for bitflips in erased pages you should not only check +- * the payload data but also their associated ECC data, because a user might +- * have programmed almost all bits to 1 but a few. In this case, we +- * shouldn't consider the chunk as erased, and checking ECC bytes prevent +- * this case. +- * 3/ The extraoob argument is optional, and should be used if some of your OOB +- * data are protected by the ECC engine. +- * It could also be used if you support subpages and want to attach some +- * extra OOB data to an ECC chunk. +- * +- * Returns a positive number of bitflips less than or equal to +- * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the +- * threshold. In case of success, the passed buffers are filled with 0xff. +- */ +-int nand_check_erased_ecc_chunk(void *data, int datalen, +- void *ecc, int ecclen, +- void *extraoob, int extraooblen, +- int bitflips_threshold) +-{ +- int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0; +- +- data_bitflips = nand_check_erased_buf(data, datalen, +- bitflips_threshold); +- if (data_bitflips < 0) +- return data_bitflips; +- +- bitflips_threshold -= data_bitflips; +- +- ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold); +- if (ecc_bitflips < 0) +- return ecc_bitflips; +- +- bitflips_threshold -= ecc_bitflips; +- +- extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen, +- bitflips_threshold); +- if (extraoob_bitflips < 0) +- return extraoob_bitflips; +- +- if (data_bitflips) +- memset(data, 0xff, datalen); +- +- if (ecc_bitflips) +- memset(ecc, 0xff, ecclen); +- +- if (extraoob_bitflips) +- memset(extraoob, 0xff, extraooblen); +- +- return data_bitflips + ecc_bitflips + extraoob_bitflips; +-} +-EXPORT_SYMBOL(nand_check_erased_ecc_chunk); +- +-/** + * nand_read_page_raw_notsupp - dummy read raw page function + * @chip: nand chip info structure + * @buf: buffer to store read data +--- a/include/linux/mtd/nand.h ++++ b/include/linux/mtd/nand.h +@@ -1136,4 +1136,9 @@ static inline bool nanddev_bbt_is_initia + int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo); + int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len); + ++int nand_check_erased_ecc_chunk(void *data, int datalen, ++ void *ecc, int ecclen, ++ void *extraoob, int extraooblen, ++ int threshold); ++ + #endif /* __LINUX_MTD_NAND_H */ +--- a/include/linux/mtd/rawnand.h ++++ b/include/linux/mtd/rawnand.h +@@ -1519,11 +1519,6 @@ int rawnand_sw_bch_correct(struct nand_c + unsigned char *read_ecc, unsigned char *calc_ecc); + void rawnand_sw_bch_cleanup(struct nand_chip *chip); + +-int nand_check_erased_ecc_chunk(void *data, int datalen, +- void *ecc, int ecclen, +- void *extraoob, int extraooblen, +- int threshold); +- + int nand_ecc_choose_conf(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail); + diff --git a/target/linux/generic/backport-6.12/404-v6.19-mtd-spinand-fmsh-remove-QE-bit-for-FM25S01A-flash.patch b/target/linux/generic/backport-6.12/404-v6.19-mtd-spinand-fmsh-remove-QE-bit-for-FM25S01A-flash.patch new file mode 100644 index 00000000000..7f9ec6aca43 --- /dev/null +++ b/target/linux/generic/backport-6.12/404-v6.19-mtd-spinand-fmsh-remove-QE-bit-for-FM25S01A-flash.patch @@ -0,0 +1,28 @@ +From a1d3bc606bf5c3b3ea811cc2019df6285d75b00f Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Mon, 3 Nov 2025 04:01:48 +0300 +Subject: [PATCH] mtd: spinand: fmsh: remove QE bit for FM25S01A flash + +According to datasheet (http://eng.fmsh.com/nvm/FM25S01A_ds_eng.pdf) +there is no QE (Quad Enable) bit for FM25S01A flash, so remove it. + +Fixes: 5f284dc15ca86 ("mtd: spinand: add support for FudanMicro FM25S01A") +Signed-off-by: Mikhail Kshevetskiy +Tested-by: Tianling Shen +Signed-off-by: Miquel Raynal +Link: Link: https://lore.kernel.org/linux-mtd/176216634975.908445.2776312239779833518.b4-ty@bootlin.com +--- + drivers/mtd/nand/spi/fmsh.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mtd/nand/spi/fmsh.c ++++ b/drivers/mtd/nand/spi/fmsh.c +@@ -58,7 +58,7 @@ static const struct spinand_info fmsh_sp + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), +- SPINAND_HAS_QE_BIT, ++ 0, + SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)), + }; + diff --git a/target/linux/generic/backport-6.12/410-01-v6.14-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch b/target/linux/generic/backport-6.12/410-01-v6.14-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch new file mode 100644 index 00000000000..1d1288e832e --- /dev/null +++ b/target/linux/generic/backport-6.12/410-01-v6.14-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch @@ -0,0 +1,1026 @@ +From 8c52932da5e6756fa66f52f0720da283fba13aa6 Mon Sep 17 00:00:00 2001 +From: Md Sadre Alam +Date: Wed, 20 Nov 2024 14:45:00 +0530 +Subject: [PATCH 1/4] mtd: rawnand: qcom: cleanup qcom_nandc driver + +Perform a global cleanup of the Qualcomm NAND +controller driver with the following improvements: + +- Remove register value indirection API + +- Remove set_reg() API + +- Convert read_loc_first & read_loc_last macro to functions + +- Rename multiple variables + +Signed-off-by: Md Sadre Alam +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/raw/qcom_nandc.c | 516 ++++++++++++++---------------- + 1 file changed, 234 insertions(+), 282 deletions(-) + +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -189,17 +189,6 @@ + #define ECC_BCH_4BIT BIT(2) + #define ECC_BCH_8BIT BIT(3) + +-#define nandc_set_read_loc_first(chip, reg, cw_offset, read_size, is_last_read_loc) \ +-nandc_set_reg(chip, reg, \ +- ((cw_offset) << READ_LOCATION_OFFSET) | \ +- ((read_size) << READ_LOCATION_SIZE) | \ +- ((is_last_read_loc) << READ_LOCATION_LAST)) +- +-#define nandc_set_read_loc_last(chip, reg, cw_offset, read_size, is_last_read_loc) \ +-nandc_set_reg(chip, reg, \ +- ((cw_offset) << READ_LOCATION_OFFSET) | \ +- ((read_size) << READ_LOCATION_SIZE) | \ +- ((is_last_read_loc) << READ_LOCATION_LAST)) + /* + * Returns the actual register address for all NAND_DEV_ registers + * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) +@@ -257,8 +246,6 @@ nandc_set_reg(chip, reg, \ + * @tx_sgl_start - start index in data sgl for tx. + * @rx_sgl_pos - current index in data sgl for rx. + * @rx_sgl_start - start index in data sgl for rx. +- * @wait_second_completion - wait for second DMA desc completion before making +- * the NAND transfer completion. + */ + struct bam_transaction { + struct bam_cmd_element *bam_ce; +@@ -275,7 +262,6 @@ struct bam_transaction { + u32 tx_sgl_start; + u32 rx_sgl_pos; + u32 rx_sgl_start; +- bool wait_second_completion; + }; + + /* +@@ -471,9 +457,9 @@ struct qcom_op { + unsigned int data_instr_idx; + unsigned int rdy_timeout_ms; + unsigned int rdy_delay_ns; +- u32 addr1_reg; +- u32 addr2_reg; +- u32 cmd_reg; ++ __le32 addr1_reg; ++ __le32 addr2_reg; ++ __le32 cmd_reg; + u8 flag; + }; + +@@ -549,17 +535,17 @@ struct qcom_nand_host { + * among different NAND controllers. + * @ecc_modes - ecc mode for NAND + * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset +- * @is_bam - whether NAND controller is using BAM +- * @is_qpic - whether NAND CTRL is part of qpic IP +- * @qpic_v2 - flag to indicate QPIC IP version 2 ++ * @supports_bam - whether NAND controller is using Bus Access Manager (BAM) ++ * @nandc_part_of_qpic - whether NAND controller is part of qpic IP ++ * @qpic_version2 - flag to indicate QPIC IP version 2 + * @use_codeword_fixup - whether NAND has different layout for boot partitions + */ + struct qcom_nandc_props { + u32 ecc_modes; + u32 dev_cmd_reg_start; +- bool is_bam; +- bool is_qpic; +- bool qpic_v2; ++ bool supports_bam; ++ bool nandc_part_of_qpic; ++ bool qpic_version2; + bool use_codeword_fixup; + }; + +@@ -613,19 +599,11 @@ static void clear_bam_transaction(struct + { + struct bam_transaction *bam_txn = nandc->bam_txn; + +- if (!nandc->props->is_bam) ++ if (!nandc->props->supports_bam) + return; + +- bam_txn->bam_ce_pos = 0; +- bam_txn->bam_ce_start = 0; +- bam_txn->cmd_sgl_pos = 0; +- bam_txn->cmd_sgl_start = 0; +- bam_txn->tx_sgl_pos = 0; +- bam_txn->tx_sgl_start = 0; +- bam_txn->rx_sgl_pos = 0; +- bam_txn->rx_sgl_start = 0; ++ memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8); + bam_txn->last_data_desc = NULL; +- bam_txn->wait_second_completion = false; + + sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * + QPIC_PER_CW_CMD_SGL); +@@ -640,46 +618,35 @@ static void qpic_bam_dma_done(void *data + { + struct bam_transaction *bam_txn = data; + +- /* +- * In case of data transfer with NAND, 2 callbacks will be generated. +- * One for command channel and another one for data channel. +- * If current transaction has data descriptors +- * (i.e. wait_second_completion is true), then set this to false +- * and wait for second DMA descriptor completion. +- */ +- if (bam_txn->wait_second_completion) +- bam_txn->wait_second_completion = false; +- else +- complete(&bam_txn->txn_done); ++ complete(&bam_txn->txn_done); + } + +-static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) ++static struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) + { + return container_of(chip, struct qcom_nand_host, chip); + } + +-static inline struct qcom_nand_controller * ++static struct qcom_nand_controller * + get_qcom_nand_controller(struct nand_chip *chip) + { + return container_of(chip->controller, struct qcom_nand_controller, + controller); + } + +-static inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset) ++static u32 nandc_read(struct qcom_nand_controller *nandc, int offset) + { + return ioread32(nandc->base + offset); + } + +-static inline void nandc_write(struct qcom_nand_controller *nandc, int offset, +- u32 val) ++static void nandc_write(struct qcom_nand_controller *nandc, int offset, ++ u32 val) + { + iowrite32(val, nandc->base + offset); + } + +-static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc, +- bool is_cpu) ++static void nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu) + { +- if (!nandc->props->is_bam) ++ if (!nandc->props->supports_bam) + return; + + if (is_cpu) +@@ -694,93 +661,90 @@ static inline void nandc_read_buffer_syn + DMA_FROM_DEVICE); + } + +-static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) +-{ +- switch (offset) { +- case NAND_FLASH_CMD: +- return ®s->cmd; +- case NAND_ADDR0: +- return ®s->addr0; +- case NAND_ADDR1: +- return ®s->addr1; +- case NAND_FLASH_CHIP_SELECT: +- return ®s->chip_sel; +- case NAND_EXEC_CMD: +- return ®s->exec; +- case NAND_FLASH_STATUS: +- return ®s->clrflashstatus; +- case NAND_DEV0_CFG0: +- return ®s->cfg0; +- case NAND_DEV0_CFG1: +- return ®s->cfg1; +- case NAND_DEV0_ECC_CFG: +- return ®s->ecc_bch_cfg; +- case NAND_READ_STATUS: +- return ®s->clrreadstatus; +- case NAND_DEV_CMD1: +- return ®s->cmd1; +- case NAND_DEV_CMD1_RESTORE: +- return ®s->orig_cmd1; +- case NAND_DEV_CMD_VLD: +- return ®s->vld; +- case NAND_DEV_CMD_VLD_RESTORE: +- return ®s->orig_vld; +- case NAND_EBI2_ECC_BUF_CFG: +- return ®s->ecc_buf_cfg; +- case NAND_READ_LOCATION_0: +- return ®s->read_location0; +- case NAND_READ_LOCATION_1: +- return ®s->read_location1; +- case NAND_READ_LOCATION_2: +- return ®s->read_location2; +- case NAND_READ_LOCATION_3: +- return ®s->read_location3; +- case NAND_READ_LOCATION_LAST_CW_0: +- return ®s->read_location_last0; +- case NAND_READ_LOCATION_LAST_CW_1: +- return ®s->read_location_last1; +- case NAND_READ_LOCATION_LAST_CW_2: +- return ®s->read_location_last2; +- case NAND_READ_LOCATION_LAST_CW_3: +- return ®s->read_location_last3; +- default: +- return NULL; +- } +-} +- +-static void nandc_set_reg(struct nand_chip *chip, int offset, +- u32 val) +-{ +- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); +- struct nandc_regs *regs = nandc->regs; +- __le32 *reg; +- +- reg = offset_to_nandc_reg(regs, offset); +- +- if (reg) +- *reg = cpu_to_le32(val); +-} +- +-/* Helper to check the code word, whether it is last cw or not */ ++/* Helper to check whether this is the last CW or not */ + static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw) + { + return cw == (ecc->steps - 1); + } + ++/** ++ * nandc_set_read_loc_first() - to set read location first register ++ * @chip: NAND Private Flash Chip Data ++ * @reg_base: location register base ++ * @cw_offset: code word offset ++ * @read_size: code word read length ++ * @is_last_read_loc: is this the last read location ++ * ++ * This function will set location register value ++ */ ++static void nandc_set_read_loc_first(struct nand_chip *chip, ++ int reg_base, u32 cw_offset, ++ u32 read_size, u32 is_last_read_loc) ++{ ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ __le32 locreg_val; ++ u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | ++ ((read_size) << READ_LOCATION_SIZE) | ++ ((is_last_read_loc) << READ_LOCATION_LAST)); ++ ++ locreg_val = cpu_to_le32(val); ++ ++ if (reg_base == NAND_READ_LOCATION_0) ++ nandc->regs->read_location0 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_1) ++ nandc->regs->read_location1 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_2) ++ nandc->regs->read_location2 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_3) ++ nandc->regs->read_location3 = locreg_val; ++} ++ ++/** ++ * nandc_set_read_loc_last - to set read location last register ++ * @chip: NAND Private Flash Chip Data ++ * @reg_base: location register base ++ * @cw_offset: code word offset ++ * @read_size: code word read length ++ * @is_last_read_loc: is this the last read location ++ * ++ * This function will set location last register value ++ */ ++static void nandc_set_read_loc_last(struct nand_chip *chip, ++ int reg_base, u32 cw_offset, ++ u32 read_size, u32 is_last_read_loc) ++{ ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ __le32 locreg_val; ++ u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | ++ ((read_size) << READ_LOCATION_SIZE) | ++ ((is_last_read_loc) << READ_LOCATION_LAST)); ++ ++ locreg_val = cpu_to_le32(val); ++ ++ if (reg_base == NAND_READ_LOCATION_LAST_CW_0) ++ nandc->regs->read_location_last0 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_LAST_CW_1) ++ nandc->regs->read_location_last1 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_LAST_CW_2) ++ nandc->regs->read_location_last2 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_LAST_CW_3) ++ nandc->regs->read_location_last3 = locreg_val; ++} ++ + /* helper to configure location register values */ + static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg, +- int cw_offset, int read_size, int is_last_read_loc) ++ u32 cw_offset, u32 read_size, u32 is_last_read_loc) + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int reg_base = NAND_READ_LOCATION_0; + +- if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw)) ++ if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) + reg_base = NAND_READ_LOCATION_LAST_CW_0; + + reg_base += reg * 4; + +- if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw)) ++ if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) + return nandc_set_read_loc_last(chip, reg_base, cw_offset, + read_size, is_last_read_loc); + else +@@ -792,12 +756,13 @@ static void nandc_set_read_loc(struct na + static void set_address(struct qcom_nand_host *host, u16 column, int page) + { + struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + +- nandc_set_reg(chip, NAND_ADDR0, page << 16 | column); +- nandc_set_reg(chip, NAND_ADDR1, page >> 16 & 0xff); ++ nandc->regs->addr0 = cpu_to_le32(page << 16 | column); ++ nandc->regs->addr1 = cpu_to_le32(page >> 16 & 0xff); + } + + /* +@@ -811,41 +776,43 @@ static void set_address(struct qcom_nand + static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, int cw) + { + struct nand_chip *chip = &host->chip; +- u32 cmd, cfg0, cfg1, ecc_bch_cfg; ++ __le32 cmd, cfg0, cfg1, ecc_bch_cfg; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + if (read) { + if (host->use_ecc) +- cmd = OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; ++ cmd = cpu_to_le32(OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE); + else +- cmd = OP_PAGE_READ | PAGE_ACC | LAST_PAGE; ++ cmd = cpu_to_le32(OP_PAGE_READ | PAGE_ACC | LAST_PAGE); + } else { +- cmd = OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; ++ cmd = cpu_to_le32(OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE); + } + + if (host->use_ecc) { +- cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) | +- (num_cw - 1) << CW_PER_PAGE; ++ cfg0 = cpu_to_le32((host->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE); + +- cfg1 = host->cfg1; +- ecc_bch_cfg = host->ecc_bch_cfg; ++ cfg1 = cpu_to_le32(host->cfg1); ++ ecc_bch_cfg = cpu_to_le32(host->ecc_bch_cfg); + } else { +- cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) | +- (num_cw - 1) << CW_PER_PAGE; ++ cfg0 = cpu_to_le32((host->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE); + +- cfg1 = host->cfg1_raw; +- ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ cfg1 = cpu_to_le32(host->cfg1_raw); ++ ecc_bch_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE); + } + +- nandc_set_reg(chip, NAND_FLASH_CMD, cmd); +- nandc_set_reg(chip, NAND_DEV0_CFG0, cfg0); +- nandc_set_reg(chip, NAND_DEV0_CFG1, cfg1); +- nandc_set_reg(chip, NAND_DEV0_ECC_CFG, ecc_bch_cfg); +- if (!nandc->props->qpic_v2) +- nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg); +- nandc_set_reg(chip, NAND_FLASH_STATUS, host->clrflashstatus); +- nandc_set_reg(chip, NAND_READ_STATUS, host->clrreadstatus); +- nandc_set_reg(chip, NAND_EXEC_CMD, 1); ++ nandc->regs->cmd = cmd; ++ nandc->regs->cfg0 = cfg0; ++ nandc->regs->cfg1 = cfg1; ++ nandc->regs->ecc_bch_cfg = ecc_bch_cfg; ++ ++ if (!nandc->props->qpic_version2) ++ nandc->regs->ecc_buf_cfg = cpu_to_le32(host->ecc_buf_cfg); ++ ++ nandc->regs->clrflashstatus = cpu_to_le32(host->clrflashstatus); ++ nandc->regs->clrreadstatus = cpu_to_le32(host->clrreadstatus); ++ nandc->regs->exec = cpu_to_le32(1); + + if (read) + nandc_set_read_loc(chip, cw, 0, 0, host->use_ecc ? +@@ -1121,7 +1088,7 @@ static int read_reg_dma(struct qcom_nand + if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) + first = dev_cmd_reg_addr(nandc, first); + +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + return prep_bam_dma_desc_cmd(nandc, true, first, vaddr, + num_regs, flags); + +@@ -1136,25 +1103,16 @@ static int read_reg_dma(struct qcom_nand + * write_reg_dma: prepares a descriptor to write a given number of + * contiguous registers + * ++ * @vaddr: contiguous memory from where register value will ++ * be written + * @first: offset of the first register in the contiguous block + * @num_regs: number of registers to write + * @flags: flags to control DMA descriptor preparation + */ +-static int write_reg_dma(struct qcom_nand_controller *nandc, int first, +- int num_regs, unsigned int flags) ++static int write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, ++ int first, int num_regs, unsigned int flags) + { + bool flow_control = false; +- struct nandc_regs *regs = nandc->regs; +- void *vaddr; +- +- vaddr = offset_to_nandc_reg(regs, first); +- +- if (first == NAND_ERASED_CW_DETECT_CFG) { +- if (flags & NAND_ERASED_CW_SET) +- vaddr = ®s->erased_cw_detect_cfg_set; +- else +- vaddr = ®s->erased_cw_detect_cfg_clr; +- } + + if (first == NAND_EXEC_CMD) + flags |= NAND_BAM_NWD; +@@ -1165,7 +1123,7 @@ static int write_reg_dma(struct qcom_nan + if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) + first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); + +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + return prep_bam_dma_desc_cmd(nandc, false, first, vaddr, + num_regs, flags); + +@@ -1188,7 +1146,7 @@ static int write_reg_dma(struct qcom_nan + static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, + const u8 *vaddr, int size, unsigned int flags) + { +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); + + return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); +@@ -1206,7 +1164,7 @@ static int read_data_dma(struct qcom_nan + static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, + const u8 *vaddr, int size, unsigned int flags) + { +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); + + return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); +@@ -1220,13 +1178,14 @@ static void config_nand_page_read(struct + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, NAND_ADDR0, 2, 0); +- write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); +- if (!nandc->props->qpic_v2) +- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); +- write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); +- write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, +- NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); ++ write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ if (!nandc->props->qpic_version2) ++ write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); ++ write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); + } + + /* +@@ -1239,16 +1198,16 @@ config_nand_cw_read(struct nand_chip *ch + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + +- int reg = NAND_READ_LOCATION_0; ++ __le32 *reg = &nandc->regs->read_location0; + +- if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw)) +- reg = NAND_READ_LOCATION_LAST_CW_0; ++ if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) ++ reg = &nandc->regs->read_location_last0; + +- if (nandc->props->is_bam) +- write_reg_dma(nandc, reg, 4, NAND_BAM_NEXT_SGL); ++ if (nandc->props->supports_bam) ++ write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + if (use_ecc) { + read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); +@@ -1279,10 +1238,10 @@ static void config_nand_page_write(struc + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, NAND_ADDR0, 2, 0); +- write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); +- if (!nandc->props->qpic_v2) +- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, ++ write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); ++ write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ if (!nandc->props->qpic_version2) ++ write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, + NAND_BAM_NEXT_SGL); + } + +@@ -1294,13 +1253,13 @@ static void config_nand_cw_write(struct + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); +- write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); ++ write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); + } + + /* helpers to submit/free our list of dma descriptors */ +@@ -1311,7 +1270,7 @@ static int submit_descs(struct qcom_nand + struct bam_transaction *bam_txn = nandc->bam_txn; + int ret = 0; + +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { + ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); + if (ret) +@@ -1336,14 +1295,9 @@ static int submit_descs(struct qcom_nand + list_for_each_entry(desc, &nandc->desc_list, node) + cookie = dmaengine_submit(desc->dma_desc); + +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + bam_txn->last_cmd_desc->callback = qpic_bam_dma_done; + bam_txn->last_cmd_desc->callback_param = bam_txn; +- if (bam_txn->last_data_desc) { +- bam_txn->last_data_desc->callback = qpic_bam_dma_done; +- bam_txn->last_data_desc->callback_param = bam_txn; +- bam_txn->wait_second_completion = true; +- } + + dma_async_issue_pending(nandc->tx_chan); + dma_async_issue_pending(nandc->rx_chan); +@@ -1365,7 +1319,7 @@ err_unmap_free_desc: + list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { + list_del(&desc->node); + +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + dma_unmap_sg(nandc->dev, desc->bam_sgl, + desc->sgl_cnt, desc->dir); + else +@@ -1382,7 +1336,7 @@ err_unmap_free_desc: + static void clear_read_regs(struct qcom_nand_controller *nandc) + { + nandc->reg_read_pos = 0; +- nandc_read_buffer_sync(nandc, false); ++ nandc_dev_to_mem(nandc, false); + } + + /* +@@ -1446,7 +1400,7 @@ static int check_flash_errors(struct qco + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int i; + +- nandc_read_buffer_sync(nandc, true); ++ nandc_dev_to_mem(nandc, true); + + for (i = 0; i < cw_cnt; i++) { + u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); +@@ -1476,7 +1430,7 @@ qcom_nandc_read_cw_raw(struct mtd_info * + clear_read_regs(nandc); + host->use_ecc = false; + +- if (nandc->props->qpic_v2) ++ if (nandc->props->qpic_version2) + raw_cw = ecc->steps - 1; + + clear_bam_transaction(nandc); +@@ -1497,7 +1451,7 @@ qcom_nandc_read_cw_raw(struct mtd_info * + oob_size2 = host->ecc_bytes_hw + host->spare_bytes; + } + +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + nandc_set_read_loc(chip, cw, 0, read_loc, data_size1, 0); + read_loc += data_size1; + +@@ -1621,7 +1575,7 @@ static int parse_read_errors(struct qcom + u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; + + buf = (struct read_stats *)nandc->reg_read_buf; +- nandc_read_buffer_sync(nandc, true); ++ nandc_dev_to_mem(nandc, true); + + for (i = 0; i < ecc->steps; i++, buf++) { + u32 flash, buffer, erased_cw; +@@ -1734,7 +1688,7 @@ static int read_page_ecc(struct qcom_nan + oob_size = host->ecc_bytes_hw + host->spare_bytes; + } + +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + if (data_buf && oob_buf) { + nandc_set_read_loc(chip, i, 0, 0, data_size, 0); + nandc_set_read_loc(chip, i, 1, data_size, +@@ -2455,14 +2409,14 @@ static int qcom_nand_attach_chip(struct + + mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); + /* Free the initially allocated BAM transaction for reading the ONFI params */ +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + free_bam_transaction(nandc); + + nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, + cwperpage); + + /* Now allocate the BAM transaction based on updated max_cwperpage */ +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + nandc->bam_txn = alloc_bam_transaction(nandc); + if (!nandc->bam_txn) { + dev_err(nandc->dev, +@@ -2522,7 +2476,7 @@ static int qcom_nand_attach_chip(struct + | ecc_mode << ECC_MODE + | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH; + +- if (!nandc->props->qpic_v2) ++ if (!nandc->props->qpic_version2) + host->ecc_buf_cfg = 0x203 << NUM_STEPS; + + host->clrflashstatus = FS_READY_BSY_N; +@@ -2556,7 +2510,7 @@ static int qcom_op_cmd_mapping(struct na + cmd = OP_FETCH_ID; + break; + case NAND_CMD_PARAM: +- if (nandc->props->qpic_v2) ++ if (nandc->props->qpic_version2) + cmd = OP_PAGE_READ_ONFI_READ; + else + cmd = OP_PAGE_READ; +@@ -2609,7 +2563,7 @@ static int qcom_parse_instructions(struc + if (ret < 0) + return ret; + +- q_op->cmd_reg = ret; ++ q_op->cmd_reg = cpu_to_le32(ret); + q_op->rdy_delay_ns = instr->delay_ns; + break; + +@@ -2619,10 +2573,10 @@ static int qcom_parse_instructions(struc + addrs = &instr->ctx.addr.addrs[offset]; + + for (i = 0; i < min_t(unsigned int, 4, naddrs); i++) +- q_op->addr1_reg |= addrs[i] << (i * 8); ++ q_op->addr1_reg |= cpu_to_le32(addrs[i] << (i * 8)); + + if (naddrs > 4) +- q_op->addr2_reg |= addrs[4]; ++ q_op->addr2_reg |= cpu_to_le32(addrs[4]); + + q_op->rdy_delay_ns = instr->delay_ns; + break; +@@ -2663,7 +2617,7 @@ static int qcom_wait_rdy_poll(struct nan + unsigned long start = jiffies + msecs_to_jiffies(time_ms); + u32 flash; + +- nandc_read_buffer_sync(nandc, true); ++ nandc_dev_to_mem(nandc, true); + + do { + flash = le32_to_cpu(nandc->reg_read_buf[0]); +@@ -2706,11 +2660,11 @@ static int qcom_read_status_exec(struct + clear_read_regs(nandc); + clear_bam_transaction(nandc); + +- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); +- nandc_set_reg(chip, NAND_EXEC_CMD, 1); ++ nandc->regs->cmd = q_op.cmd_reg; ++ nandc->regs->exec = cpu_to_le32(1); + +- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + ret = submit_descs(nandc); +@@ -2719,7 +2673,7 @@ static int qcom_read_status_exec(struct + goto err_out; + } + +- nandc_read_buffer_sync(nandc, true); ++ nandc_dev_to_mem(nandc, true); + + for (i = 0; i < num_cw; i++) { + flash_status = le32_to_cpu(nandc->reg_read_buf[i]); +@@ -2763,16 +2717,14 @@ static int qcom_read_id_type_exec(struct + clear_read_regs(nandc); + clear_bam_transaction(nandc); + +- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); +- nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg); +- nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg); +- nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT, +- nandc->props->is_bam ? 0 : DM_EN); ++ nandc->regs->cmd = q_op.cmd_reg; ++ nandc->regs->addr0 = q_op.addr1_reg; ++ nandc->regs->addr1 = q_op.addr2_reg; ++ nandc->regs->chip_sel = cpu_to_le32(nandc->props->supports_bam ? 0 : DM_EN); ++ nandc->regs->exec = cpu_to_le32(1); + +- nandc_set_reg(chip, NAND_EXEC_CMD, 1); +- +- write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); + +@@ -2786,7 +2738,7 @@ static int qcom_read_id_type_exec(struct + op_id = q_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + +- nandc_read_buffer_sync(nandc, true); ++ nandc_dev_to_mem(nandc, true); + memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len); + + err_out: +@@ -2807,15 +2759,14 @@ static int qcom_misc_cmd_type_exec(struc + + if (q_op.flag == OP_PROGRAM_PAGE) { + goto wait_rdy; +- } else if (q_op.cmd_reg == OP_BLOCK_ERASE) { +- q_op.cmd_reg |= PAGE_ACC | LAST_PAGE; +- nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg); +- nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg); +- nandc_set_reg(chip, NAND_DEV0_CFG0, +- host->cfg0_raw & ~(7 << CW_PER_PAGE)); +- nandc_set_reg(chip, NAND_DEV0_CFG1, host->cfg1_raw); ++ } else if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE)) { ++ q_op.cmd_reg |= cpu_to_le32(PAGE_ACC | LAST_PAGE); ++ nandc->regs->addr0 = q_op.addr1_reg; ++ nandc->regs->addr1 = q_op.addr2_reg; ++ nandc->regs->cfg0 = cpu_to_le32(host->cfg0_raw & ~(7 << CW_PER_PAGE)); ++ nandc->regs->cfg1 = cpu_to_le32(host->cfg1_raw); + instrs = 3; +- } else if (q_op.cmd_reg != OP_RESET_DEVICE) { ++ } else if (q_op.cmd_reg != cpu_to_le32(OP_RESET_DEVICE)) { + return 0; + } + +@@ -2826,14 +2777,14 @@ static int qcom_misc_cmd_type_exec(struc + clear_read_regs(nandc); + clear_bam_transaction(nandc); + +- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); +- nandc_set_reg(chip, NAND_EXEC_CMD, 1); ++ nandc->regs->cmd = q_op.cmd_reg; ++ nandc->regs->exec = cpu_to_le32(1); + +- write_reg_dma(nandc, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL); +- if (q_op.cmd_reg == OP_BLOCK_ERASE) +- write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL); ++ if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE)) ++ write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + ret = submit_descs(nandc); +@@ -2862,14 +2813,14 @@ static int qcom_param_page_type_exec(str + + reg_base = NAND_READ_LOCATION_0; + +- if (nandc->props->qpic_v2) ++ if (nandc->props->qpic_version2) + reg_base = NAND_READ_LOCATION_LAST_CW_0; + + ret = qcom_parse_instructions(chip, subop, &q_op); + if (ret) + return ret; + +- q_op.cmd_reg |= PAGE_ACC | LAST_PAGE; ++ q_op.cmd_reg |= cpu_to_le32(PAGE_ACC | LAST_PAGE); + + nandc->buf_count = 0; + nandc->buf_start = 0; +@@ -2877,52 +2828,52 @@ static int qcom_param_page_type_exec(str + clear_read_regs(nandc); + clear_bam_transaction(nandc); + +- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); ++ nandc->regs->cmd = q_op.cmd_reg; ++ nandc->regs->addr0 = 0; ++ nandc->regs->addr1 = 0; ++ ++ nandc->regs->cfg0 = cpu_to_le32(0 << CW_PER_PAGE | ++ 512 << UD_SIZE_BYTES | ++ 5 << NUM_ADDR_CYCLES | ++ 0 << SPARE_SIZE_BYTES); ++ ++ nandc->regs->cfg1 = cpu_to_le32(7 << NAND_RECOVERY_CYCLES | ++ 0 << CS_ACTIVE_BSY | ++ 17 << BAD_BLOCK_BYTE_NUM | ++ 1 << BAD_BLOCK_IN_SPARE_AREA | ++ 2 << WR_RD_BSY_GAP | ++ 0 << WIDE_FLASH | ++ 1 << DEV0_CFG1_ECC_DISABLE); + +- nandc_set_reg(chip, NAND_ADDR0, 0); +- nandc_set_reg(chip, NAND_ADDR1, 0); +- nandc_set_reg(chip, NAND_DEV0_CFG0, 0 << CW_PER_PAGE +- | 512 << UD_SIZE_BYTES +- | 5 << NUM_ADDR_CYCLES +- | 0 << SPARE_SIZE_BYTES); +- nandc_set_reg(chip, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES +- | 0 << CS_ACTIVE_BSY +- | 17 << BAD_BLOCK_BYTE_NUM +- | 1 << BAD_BLOCK_IN_SPARE_AREA +- | 2 << WR_RD_BSY_GAP +- | 0 << WIDE_FLASH +- | 1 << DEV0_CFG1_ECC_DISABLE); +- if (!nandc->props->qpic_v2) +- nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); ++ if (!nandc->props->qpic_version2) ++ nandc->regs->ecc_buf_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE); + + /* configure CMD1 and VLD for ONFI param probing in QPIC v1 */ +- if (!nandc->props->qpic_v2) { +- nandc_set_reg(chip, NAND_DEV_CMD_VLD, +- (nandc->vld & ~READ_START_VLD)); +- nandc_set_reg(chip, NAND_DEV_CMD1, +- (nandc->cmd1 & ~(0xFF << READ_ADDR)) +- | NAND_CMD_PARAM << READ_ADDR); ++ if (!nandc->props->qpic_version2) { ++ nandc->regs->vld = cpu_to_le32((nandc->vld & ~READ_START_VLD)); ++ nandc->regs->cmd1 = cpu_to_le32((nandc->cmd1 & ~(0xFF << READ_ADDR)) ++ | NAND_CMD_PARAM << READ_ADDR); + } + +- nandc_set_reg(chip, NAND_EXEC_CMD, 1); +- +- if (!nandc->props->qpic_v2) { +- nandc_set_reg(chip, NAND_DEV_CMD1_RESTORE, nandc->cmd1); +- nandc_set_reg(chip, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); ++ nandc->regs->exec = cpu_to_le32(1); ++ ++ if (!nandc->props->qpic_version2) { ++ nandc->regs->orig_cmd1 = cpu_to_le32(nandc->cmd1); ++ nandc->regs->orig_vld = cpu_to_le32(nandc->vld); + } + + instr = q_op.data_instr; + op_id = q_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + +- if (nandc->props->qpic_v2) ++ if (nandc->props->qpic_version2) + nandc_set_read_loc_last(chip, reg_base, 0, len, 1); + else + nandc_set_read_loc_first(chip, reg_base, 0, len, 1); + +- if (!nandc->props->qpic_v2) { +- write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); +- write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); ++ if (!nandc->props->qpic_version2) { ++ write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0); ++ write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); + } + + nandc->buf_count = 512; +@@ -2934,9 +2885,10 @@ static int qcom_param_page_type_exec(str + nandc->buf_count, 0); + + /* restore CMD1 and VLD regs */ +- if (!nandc->props->qpic_v2) { +- write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); +- write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); ++ if (!nandc->props->qpic_version2) { ++ write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0); ++ write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1, ++ NAND_BAM_NEXT_SGL); + } + + ret = submit_descs(nandc); +@@ -3025,7 +2977,7 @@ static const struct nand_controller_ops + + static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) + { +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) + dma_unmap_single(nandc->dev, nandc->reg_read_dma, + MAX_REG_RD * +@@ -3078,7 +3030,7 @@ static int qcom_nandc_alloc(struct qcom_ + if (!nandc->reg_read_buf) + return -ENOMEM; + +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + nandc->reg_read_dma = + dma_map_single(nandc->dev, nandc->reg_read_buf, + MAX_REG_RD * +@@ -3159,15 +3111,15 @@ static int qcom_nandc_setup(struct qcom_ + u32 nand_ctrl; + + /* kill onenand */ +- if (!nandc->props->is_qpic) ++ if (!nandc->props->nandc_part_of_qpic) + nandc_write(nandc, SFLASHC_BURST_CFG, 0); + +- if (!nandc->props->qpic_v2) ++ if (!nandc->props->qpic_version2) + nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), + NAND_DEV_CMD_VLD_VAL); + + /* enable ADM or BAM DMA */ +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + nand_ctrl = nandc_read(nandc, NAND_CTRL); + + /* +@@ -3184,7 +3136,7 @@ static int qcom_nandc_setup(struct qcom_ + } + + /* save the original values of these registers */ +- if (!nandc->props->qpic_v2) { ++ if (!nandc->props->qpic_version2) { + nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1)); + nandc->vld = NAND_DEV_CMD_VLD_VAL; + } +@@ -3357,7 +3309,7 @@ static int qcom_nandc_parse_dt(struct pl + struct device_node *np = nandc->dev->of_node; + int ret; + +- if (!nandc->props->is_bam) { ++ if (!nandc->props->supports_bam) { + ret = of_property_read_u32(np, "qcom,cmd-crci", + &nandc->cmd_crci); + if (ret) { +@@ -3482,30 +3434,30 @@ static void qcom_nandc_remove(struct pla + + static const struct qcom_nandc_props ipq806x_nandc_props = { + .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), +- .is_bam = false, ++ .supports_bam = false, + .use_codeword_fixup = true, + .dev_cmd_reg_start = 0x0, + }; + + static const struct qcom_nandc_props ipq4019_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), +- .is_bam = true, +- .is_qpic = true, ++ .supports_bam = true, ++ .nandc_part_of_qpic = true, + .dev_cmd_reg_start = 0x0, + }; + + static const struct qcom_nandc_props ipq8074_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), +- .is_bam = true, +- .is_qpic = true, ++ .supports_bam = true, ++ .nandc_part_of_qpic = true, + .dev_cmd_reg_start = 0x7000, + }; + + static const struct qcom_nandc_props sdx55_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), +- .is_bam = true, +- .is_qpic = true, +- .qpic_v2 = true, ++ .supports_bam = true, ++ .nandc_part_of_qpic = true, ++ .qpic_version2 = true, + .dev_cmd_reg_start = 0x7000, + }; + diff --git a/target/linux/generic/backport-6.12/410-02-v6.14-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch b/target/linux/generic/backport-6.12/410-02-v6.14-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch new file mode 100644 index 00000000000..23ef86f8077 --- /dev/null +++ b/target/linux/generic/backport-6.12/410-02-v6.14-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch @@ -0,0 +1,880 @@ +From 1d479f5b345e0c3650fec4dddeef9fc6fab30c8b Mon Sep 17 00:00:00 2001 +From: Md Sadre Alam +Date: Wed, 20 Nov 2024 14:45:01 +0530 +Subject: [PATCH 2/4] mtd: rawnand: qcom: Add qcom prefix to common api + +Add qcom prefix to all the api which will be commonly +used by spi nand driver and raw nand driver. + +Reviewed-by: Konrad Dybcio +Signed-off-by: Md Sadre Alam +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/raw/qcom_nandc.c | 320 +++++++++++++++--------------- + 1 file changed, 160 insertions(+), 160 deletions(-) + +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -53,7 +53,7 @@ + #define NAND_READ_LOCATION_LAST_CW_2 0xf48 + #define NAND_READ_LOCATION_LAST_CW_3 0xf4c + +-/* dummy register offsets, used by write_reg_dma */ ++/* dummy register offsets, used by qcom_write_reg_dma */ + #define NAND_DEV_CMD1_RESTORE 0xdead + #define NAND_DEV_CMD_VLD_RESTORE 0xbeef + +@@ -211,7 +211,7 @@ + + /* + * Flags used in DMA descriptor preparation helper functions +- * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) ++ * (i.e. qcom_read_reg_dma/qcom_write_reg_dma/qcom_read_data_dma/qcom_write_data_dma) + */ + /* Don't set the EOT in current tx BAM sgl */ + #define NAND_BAM_NO_EOT BIT(0) +@@ -550,7 +550,7 @@ struct qcom_nandc_props { + }; + + /* Frees the BAM transaction memory */ +-static void free_bam_transaction(struct qcom_nand_controller *nandc) ++static void qcom_free_bam_transaction(struct qcom_nand_controller *nandc) + { + struct bam_transaction *bam_txn = nandc->bam_txn; + +@@ -559,7 +559,7 @@ static void free_bam_transaction(struct + + /* Allocates and Initializes the BAM transaction */ + static struct bam_transaction * +-alloc_bam_transaction(struct qcom_nand_controller *nandc) ++qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc) + { + struct bam_transaction *bam_txn; + size_t bam_txn_size; +@@ -595,7 +595,7 @@ alloc_bam_transaction(struct qcom_nand_c + } + + /* Clears the BAM transaction indexes */ +-static void clear_bam_transaction(struct qcom_nand_controller *nandc) ++static void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc) + { + struct bam_transaction *bam_txn = nandc->bam_txn; + +@@ -614,7 +614,7 @@ static void clear_bam_transaction(struct + } + + /* Callback for DMA descriptor completion */ +-static void qpic_bam_dma_done(void *data) ++static void qcom_qpic_bam_dma_done(void *data) + { + struct bam_transaction *bam_txn = data; + +@@ -644,7 +644,7 @@ static void nandc_write(struct qcom_nand + iowrite32(val, nandc->base + offset); + } + +-static void nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu) ++static void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu) + { + if (!nandc->props->supports_bam) + return; +@@ -824,9 +824,9 @@ static void update_rw_regs(struct qcom_n + * for BAM. This descriptor will be added in the NAND DMA descriptor queue + * which will be submitted to DMA engine. + */ +-static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, +- struct dma_chan *chan, +- unsigned long flags) ++static int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc, ++ struct dma_chan *chan, ++ unsigned long flags) + { + struct desc_info *desc; + struct scatterlist *sgl; +@@ -903,9 +903,9 @@ static int prepare_bam_async_desc(struct + * NAND_BAM_NEXT_SGL will be used for starting the separate SGL + * after the current command element. + */ +-static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, +- int reg_off, const void *vaddr, +- int size, unsigned int flags) ++static int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, ++ int size, unsigned int flags) + { + int bam_ce_size; + int i, ret; +@@ -943,9 +943,9 @@ static int prep_bam_dma_desc_cmd(struct + bam_txn->bam_ce_start = bam_txn->bam_ce_pos; + + if (flags & NAND_BAM_NWD) { +- ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, +- DMA_PREP_FENCE | +- DMA_PREP_CMD); ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, ++ DMA_PREP_FENCE | ++ DMA_PREP_CMD); + if (ret) + return ret; + } +@@ -958,9 +958,8 @@ static int prep_bam_dma_desc_cmd(struct + * Prepares the data descriptor for BAM DMA which will be used for NAND + * data reads and writes. + */ +-static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, +- const void *vaddr, +- int size, unsigned int flags) ++static int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, ++ const void *vaddr, int size, unsigned int flags) + { + int ret; + struct bam_transaction *bam_txn = nandc->bam_txn; +@@ -979,8 +978,8 @@ static int prep_bam_dma_desc_data(struct + * is not set, form the DMA descriptor + */ + if (!(flags & NAND_BAM_NO_EOT)) { +- ret = prepare_bam_async_desc(nandc, nandc->tx_chan, +- DMA_PREP_INTERRUPT); ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, ++ DMA_PREP_INTERRUPT); + if (ret) + return ret; + } +@@ -989,9 +988,9 @@ static int prep_bam_dma_desc_data(struct + return 0; + } + +-static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, +- int reg_off, const void *vaddr, int size, +- bool flow_control) ++static int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, int size, ++ bool flow_control) + { + struct desc_info *desc; + struct dma_async_tx_descriptor *dma_desc; +@@ -1069,15 +1068,15 @@ err: + } + + /* +- * read_reg_dma: prepares a descriptor to read a given number of ++ * qcom_read_reg_dma: prepares a descriptor to read a given number of + * contiguous registers to the reg_read_buf pointer + * + * @first: offset of the first register in the contiguous block + * @num_regs: number of registers to read + * @flags: flags to control DMA descriptor preparation + */ +-static int read_reg_dma(struct qcom_nand_controller *nandc, int first, +- int num_regs, unsigned int flags) ++static int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first, ++ int num_regs, unsigned int flags) + { + bool flow_control = false; + void *vaddr; +@@ -1089,18 +1088,18 @@ static int read_reg_dma(struct qcom_nand + first = dev_cmd_reg_addr(nandc, first); + + if (nandc->props->supports_bam) +- return prep_bam_dma_desc_cmd(nandc, true, first, vaddr, ++ return qcom_prep_bam_dma_desc_cmd(nandc, true, first, vaddr, + num_regs, flags); + + if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) + flow_control = true; + +- return prep_adm_dma_desc(nandc, true, first, vaddr, ++ return qcom_prep_adm_dma_desc(nandc, true, first, vaddr, + num_regs * sizeof(u32), flow_control); + } + + /* +- * write_reg_dma: prepares a descriptor to write a given number of ++ * qcom_write_reg_dma: prepares a descriptor to write a given number of + * contiguous registers + * + * @vaddr: contiguous memory from where register value will +@@ -1109,8 +1108,8 @@ static int read_reg_dma(struct qcom_nand + * @num_regs: number of registers to write + * @flags: flags to control DMA descriptor preparation + */ +-static int write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, +- int first, int num_regs, unsigned int flags) ++static int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, ++ int first, int num_regs, unsigned int flags) + { + bool flow_control = false; + +@@ -1124,18 +1123,18 @@ static int write_reg_dma(struct qcom_nan + first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); + + if (nandc->props->supports_bam) +- return prep_bam_dma_desc_cmd(nandc, false, first, vaddr, ++ return qcom_prep_bam_dma_desc_cmd(nandc, false, first, vaddr, + num_regs, flags); + + if (first == NAND_FLASH_CMD) + flow_control = true; + +- return prep_adm_dma_desc(nandc, false, first, vaddr, ++ return qcom_prep_adm_dma_desc(nandc, false, first, vaddr, + num_regs * sizeof(u32), flow_control); + } + + /* +- * read_data_dma: prepares a DMA descriptor to transfer data from the ++ * qcom_read_data_dma: prepares a DMA descriptor to transfer data from the + * controller's internal buffer to the buffer 'vaddr' + * + * @reg_off: offset within the controller's data buffer +@@ -1143,17 +1142,17 @@ static int write_reg_dma(struct qcom_nan + * @size: DMA transaction size in bytes + * @flags: flags to control DMA descriptor preparation + */ +-static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, +- const u8 *vaddr, int size, unsigned int flags) ++static int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off, ++ const u8 *vaddr, int size, unsigned int flags) + { + if (nandc->props->supports_bam) +- return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); ++ return qcom_prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); + +- return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); ++ return qcom_prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); + } + + /* +- * write_data_dma: prepares a DMA descriptor to transfer data from ++ * qcom_write_data_dma: prepares a DMA descriptor to transfer data from + * 'vaddr' to the controller's internal buffer + * + * @reg_off: offset within the controller's data buffer +@@ -1161,13 +1160,13 @@ static int read_data_dma(struct qcom_nan + * @size: DMA transaction size in bytes + * @flags: flags to control DMA descriptor preparation + */ +-static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, +- const u8 *vaddr, int size, unsigned int flags) ++static int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off, ++ const u8 *vaddr, int size, unsigned int flags) + { + if (nandc->props->supports_bam) +- return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); ++ return qcom_prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); + +- return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); ++ return qcom_prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); + } + + /* +@@ -1178,14 +1177,14 @@ static void config_nand_page_read(struct + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); +- write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); + if (!nandc->props->qpic_version2) +- write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); +- write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr, +- NAND_ERASED_CW_DETECT_CFG, 1, 0); +- write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set, +- NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); + } + + /* +@@ -1204,17 +1203,17 @@ config_nand_cw_read(struct nand_chip *ch + reg = &nandc->regs->read_location_last0; + + if (nandc->props->supports_bam) +- write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + if (use_ecc) { +- read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); +- read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, +- NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); ++ qcom_read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, ++ NAND_BAM_NEXT_SGL); + } else { +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + } + } + +@@ -1238,11 +1237,11 @@ static void config_nand_page_write(struc + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); +- write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); + if (!nandc->props->qpic_version2) +- write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, +- NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, ++ NAND_BAM_NEXT_SGL); + } + + /* +@@ -1253,17 +1252,18 @@ static void config_nand_cw_write(struct + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); +- write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, ++ NAND_BAM_NEXT_SGL); + } + + /* helpers to submit/free our list of dma descriptors */ +-static int submit_descs(struct qcom_nand_controller *nandc) ++static int qcom_submit_descs(struct qcom_nand_controller *nandc) + { + struct desc_info *desc, *n; + dma_cookie_t cookie = 0; +@@ -1272,21 +1272,21 @@ static int submit_descs(struct qcom_nand + + if (nandc->props->supports_bam) { + if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { +- ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->rx_chan, 0); + if (ret) + goto err_unmap_free_desc; + } + + if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { +- ret = prepare_bam_async_desc(nandc, nandc->tx_chan, +- DMA_PREP_INTERRUPT); ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, ++ DMA_PREP_INTERRUPT); + if (ret) + goto err_unmap_free_desc; + } + + if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { +- ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, +- DMA_PREP_CMD); ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, ++ DMA_PREP_CMD); + if (ret) + goto err_unmap_free_desc; + } +@@ -1296,7 +1296,7 @@ static int submit_descs(struct qcom_nand + cookie = dmaengine_submit(desc->dma_desc); + + if (nandc->props->supports_bam) { +- bam_txn->last_cmd_desc->callback = qpic_bam_dma_done; ++ bam_txn->last_cmd_desc->callback = qcom_qpic_bam_dma_done; + bam_txn->last_cmd_desc->callback_param = bam_txn; + + dma_async_issue_pending(nandc->tx_chan); +@@ -1314,7 +1314,7 @@ static int submit_descs(struct qcom_nand + err_unmap_free_desc: + /* + * Unmap the dma sg_list and free the desc allocated by both +- * prepare_bam_async_desc() and prep_adm_dma_desc() functions. ++ * qcom_prepare_bam_async_desc() and qcom_prep_adm_dma_desc() functions. + */ + list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { + list_del(&desc->node); +@@ -1333,10 +1333,10 @@ err_unmap_free_desc: + } + + /* reset the register read buffer for next NAND operation */ +-static void clear_read_regs(struct qcom_nand_controller *nandc) ++static void qcom_clear_read_regs(struct qcom_nand_controller *nandc) + { + nandc->reg_read_pos = 0; +- nandc_dev_to_mem(nandc, false); ++ qcom_nandc_dev_to_mem(nandc, false); + } + + /* +@@ -1400,7 +1400,7 @@ static int check_flash_errors(struct qco + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int i; + +- nandc_dev_to_mem(nandc, true); ++ qcom_nandc_dev_to_mem(nandc, true); + + for (i = 0; i < cw_cnt; i++) { + u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); +@@ -1427,13 +1427,13 @@ qcom_nandc_read_cw_raw(struct mtd_info * + nand_read_page_op(chip, page, 0, NULL, 0); + nandc->buf_count = 0; + nandc->buf_start = 0; +- clear_read_regs(nandc); ++ qcom_clear_read_regs(nandc); + host->use_ecc = false; + + if (nandc->props->qpic_version2) + raw_cw = ecc->steps - 1; + +- clear_bam_transaction(nandc); ++ qcom_clear_bam_transaction(nandc); + set_address(host, host->cw_size * cw, page); + update_rw_regs(host, 1, true, raw_cw); + config_nand_page_read(chip); +@@ -1466,18 +1466,18 @@ qcom_nandc_read_cw_raw(struct mtd_info * + + config_nand_cw_read(chip, false, raw_cw); + +- read_data_dma(nandc, reg_off, data_buf, data_size1, 0); ++ qcom_read_data_dma(nandc, reg_off, data_buf, data_size1, 0); + reg_off += data_size1; + +- read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); ++ qcom_read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); + reg_off += oob_size1; + +- read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0); ++ qcom_read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0); + reg_off += data_size2; + +- read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0); ++ qcom_read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to read raw cw %d\n", cw); + return ret; +@@ -1575,7 +1575,7 @@ static int parse_read_errors(struct qcom + u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; + + buf = (struct read_stats *)nandc->reg_read_buf; +- nandc_dev_to_mem(nandc, true); ++ qcom_nandc_dev_to_mem(nandc, true); + + for (i = 0; i < ecc->steps; i++, buf++) { + u32 flash, buffer, erased_cw; +@@ -1704,8 +1704,8 @@ static int read_page_ecc(struct qcom_nan + config_nand_cw_read(chip, true, i); + + if (data_buf) +- read_data_dma(nandc, FLASH_BUF_ACC, data_buf, +- data_size, 0); ++ qcom_read_data_dma(nandc, FLASH_BUF_ACC, data_buf, ++ data_size, 0); + + /* + * when ecc is enabled, the controller doesn't read the real +@@ -1720,8 +1720,8 @@ static int read_page_ecc(struct qcom_nan + for (j = 0; j < host->bbm_size; j++) + *oob_buf++ = 0xff; + +- read_data_dma(nandc, FLASH_BUF_ACC + data_size, +- oob_buf, oob_size, 0); ++ qcom_read_data_dma(nandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); + } + + if (data_buf) +@@ -1730,7 +1730,7 @@ static int read_page_ecc(struct qcom_nan + oob_buf += oob_size; + } + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to read page/oob\n"); + return ret; +@@ -1751,7 +1751,7 @@ static int copy_last_cw(struct qcom_nand + int size; + int ret; + +- clear_read_regs(nandc); ++ qcom_clear_read_regs(nandc); + + size = host->use_ecc ? host->cw_data : host->cw_size; + +@@ -1763,9 +1763,9 @@ static int copy_last_cw(struct qcom_nand + + config_nand_single_cw_page_read(chip, host->use_ecc, ecc->steps - 1); + +- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); ++ qcom_read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) + dev_err(nandc->dev, "failed to copy last codeword\n"); + +@@ -1851,14 +1851,14 @@ static int qcom_nandc_read_page(struct n + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = true; +- clear_read_regs(nandc); ++ qcom_clear_read_regs(nandc); + set_address(host, 0, page); + update_rw_regs(host, ecc->steps, true, 0); + + data_buf = buf; + oob_buf = oob_required ? chip->oob_poi : NULL; + +- clear_bam_transaction(nandc); ++ qcom_clear_bam_transaction(nandc); + + return read_page_ecc(host, data_buf, oob_buf, page); + } +@@ -1899,8 +1899,8 @@ static int qcom_nandc_read_oob(struct na + if (host->nr_boot_partitions) + qcom_nandc_codeword_fixup(host, page); + +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + host->use_ecc = true; + set_address(host, 0, page); +@@ -1927,8 +1927,8 @@ static int qcom_nandc_write_page(struct + set_address(host, 0, page); + nandc->buf_count = 0; + nandc->buf_start = 0; +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + data_buf = (u8 *)buf; + oob_buf = chip->oob_poi; +@@ -1949,8 +1949,8 @@ static int qcom_nandc_write_page(struct + oob_size = ecc->bytes; + } + +- write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, +- i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0); ++ qcom_write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, ++ i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0); + + /* + * when ECC is enabled, we don't really need to write anything +@@ -1962,8 +1962,8 @@ static int qcom_nandc_write_page(struct + if (qcom_nandc_is_last_cw(ecc, i)) { + oob_buf += host->bbm_size; + +- write_data_dma(nandc, FLASH_BUF_ACC + data_size, +- oob_buf, oob_size, 0); ++ qcom_write_data_dma(nandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); + } + + config_nand_cw_write(chip); +@@ -1972,7 +1972,7 @@ static int qcom_nandc_write_page(struct + oob_buf += oob_size; + } + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to write page\n"); + return ret; +@@ -1997,8 +1997,8 @@ static int qcom_nandc_write_page_raw(str + qcom_nandc_codeword_fixup(host, page); + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + data_buf = (u8 *)buf; + oob_buf = chip->oob_poi; +@@ -2024,28 +2024,28 @@ static int qcom_nandc_write_page_raw(str + oob_size2 = host->ecc_bytes_hw + host->spare_bytes; + } + +- write_data_dma(nandc, reg_off, data_buf, data_size1, +- NAND_BAM_NO_EOT); ++ qcom_write_data_dma(nandc, reg_off, data_buf, data_size1, ++ NAND_BAM_NO_EOT); + reg_off += data_size1; + data_buf += data_size1; + +- write_data_dma(nandc, reg_off, oob_buf, oob_size1, +- NAND_BAM_NO_EOT); ++ qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size1, ++ NAND_BAM_NO_EOT); + reg_off += oob_size1; + oob_buf += oob_size1; + +- write_data_dma(nandc, reg_off, data_buf, data_size2, +- NAND_BAM_NO_EOT); ++ qcom_write_data_dma(nandc, reg_off, data_buf, data_size2, ++ NAND_BAM_NO_EOT); + reg_off += data_size2; + data_buf += data_size2; + +- write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); ++ qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); + oob_buf += oob_size2; + + config_nand_cw_write(chip); + } + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to write raw page\n"); + return ret; +@@ -2075,7 +2075,7 @@ static int qcom_nandc_write_oob(struct n + qcom_nandc_codeword_fixup(host, page); + + host->use_ecc = true; +- clear_bam_transaction(nandc); ++ qcom_clear_bam_transaction(nandc); + + /* calculate the data and oob size for the last codeword/step */ + data_size = ecc->size - ((ecc->steps - 1) << 2); +@@ -2090,11 +2090,11 @@ static int qcom_nandc_write_oob(struct n + update_rw_regs(host, 1, false, 0); + + config_nand_page_write(chip); +- write_data_dma(nandc, FLASH_BUF_ACC, +- nandc->data_buffer, data_size + oob_size, 0); ++ qcom_write_data_dma(nandc, FLASH_BUF_ACC, ++ nandc->data_buffer, data_size + oob_size, 0); + config_nand_cw_write(chip); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to write oob\n"); + return ret; +@@ -2121,7 +2121,7 @@ static int qcom_nandc_block_bad(struct n + */ + host->use_ecc = false; + +- clear_bam_transaction(nandc); ++ qcom_clear_bam_transaction(nandc); + ret = copy_last_cw(host, page); + if (ret) + goto err; +@@ -2148,8 +2148,8 @@ static int qcom_nandc_block_markbad(stru + struct nand_ecc_ctrl *ecc = &chip->ecc; + int page, ret; + +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + /* + * to mark the BBM as bad, we flash the entire last codeword with 0s. +@@ -2166,11 +2166,11 @@ static int qcom_nandc_block_markbad(stru + update_rw_regs(host, 1, false, ecc->steps - 1); + + config_nand_page_write(chip); +- write_data_dma(nandc, FLASH_BUF_ACC, +- nandc->data_buffer, host->cw_size, 0); ++ qcom_write_data_dma(nandc, FLASH_BUF_ACC, ++ nandc->data_buffer, host->cw_size, 0); + config_nand_cw_write(chip); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to update BBM\n"); + return ret; +@@ -2410,14 +2410,14 @@ static int qcom_nand_attach_chip(struct + mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); + /* Free the initially allocated BAM transaction for reading the ONFI params */ + if (nandc->props->supports_bam) +- free_bam_transaction(nandc); ++ qcom_free_bam_transaction(nandc); + + nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, + cwperpage); + + /* Now allocate the BAM transaction based on updated max_cwperpage */ + if (nandc->props->supports_bam) { +- nandc->bam_txn = alloc_bam_transaction(nandc); ++ nandc->bam_txn = qcom_alloc_bam_transaction(nandc); + if (!nandc->bam_txn) { + dev_err(nandc->dev, + "failed to allocate bam transaction\n"); +@@ -2617,7 +2617,7 @@ static int qcom_wait_rdy_poll(struct nan + unsigned long start = jiffies + msecs_to_jiffies(time_ms); + u32 flash; + +- nandc_dev_to_mem(nandc, true); ++ qcom_nandc_dev_to_mem(nandc, true); + + do { + flash = le32_to_cpu(nandc->reg_read_buf[0]); +@@ -2657,23 +2657,23 @@ static int qcom_read_status_exec(struct + nandc->buf_start = 0; + host->use_ecc = false; + +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + nandc->regs->cmd = q_op.cmd_reg; + nandc->regs->exec = cpu_to_le32(1); + +- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting status descriptor\n"); + goto err_out; + } + +- nandc_dev_to_mem(nandc, true); ++ qcom_nandc_dev_to_mem(nandc, true); + + for (i = 0; i < num_cw; i++) { + flash_status = le32_to_cpu(nandc->reg_read_buf[i]); +@@ -2714,8 +2714,8 @@ static int qcom_read_id_type_exec(struct + nandc->buf_start = 0; + host->use_ecc = false; + +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + nandc->regs->cmd = q_op.cmd_reg; + nandc->regs->addr0 = q_op.addr1_reg; +@@ -2723,12 +2723,12 @@ static int qcom_read_id_type_exec(struct + nandc->regs->chip_sel = cpu_to_le32(nandc->props->supports_bam ? 0 : DM_EN); + nandc->regs->exec = cpu_to_le32(1); + +- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + +- read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting read id descriptor\n"); + goto err_out; +@@ -2738,7 +2738,7 @@ static int qcom_read_id_type_exec(struct + op_id = q_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + +- nandc_dev_to_mem(nandc, true); ++ qcom_nandc_dev_to_mem(nandc, true); + memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len); + + err_out: +@@ -2774,20 +2774,20 @@ static int qcom_misc_cmd_type_exec(struc + nandc->buf_start = 0; + host->use_ecc = false; + +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + nandc->regs->cmd = q_op.cmd_reg; + nandc->regs->exec = cpu_to_le32(1); + +- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL); + if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE)) +- write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting misc descriptor\n"); + goto err_out; +@@ -2825,8 +2825,8 @@ static int qcom_param_page_type_exec(str + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = false; +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + nandc->regs->cmd = q_op.cmd_reg; + nandc->regs->addr0 = 0; +@@ -2872,8 +2872,8 @@ static int qcom_param_page_type_exec(str + nandc_set_read_loc_first(chip, reg_base, 0, len, 1); + + if (!nandc->props->qpic_version2) { +- write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0); +- write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); + } + + nandc->buf_count = 512; +@@ -2881,17 +2881,17 @@ static int qcom_param_page_type_exec(str + + config_nand_single_cw_page_read(chip, false, 0); + +- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, +- nandc->buf_count, 0); ++ qcom_read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, ++ nandc->buf_count, 0); + + /* restore CMD1 and VLD regs */ + if (!nandc->props->qpic_version2) { +- write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0); +- write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1, +- NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1, ++ NAND_BAM_NEXT_SGL); + } + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting param page descriptor\n"); + goto err_out; +@@ -3075,7 +3075,7 @@ static int qcom_nandc_alloc(struct qcom_ + * maximum codeword size + */ + nandc->max_cwperpage = 1; +- nandc->bam_txn = alloc_bam_transaction(nandc); ++ nandc->bam_txn = qcom_alloc_bam_transaction(nandc); + if (!nandc->bam_txn) { + dev_err(nandc->dev, + "failed to allocate bam transaction\n"); diff --git a/target/linux/generic/backport-6.12/410-03-v6.14-mtd-nand-Add-qpic_common-API-file.patch b/target/linux/generic/backport-6.12/410-03-v6.14-mtd-nand-Add-qpic_common-API-file.patch new file mode 100644 index 00000000000..9272f988627 --- /dev/null +++ b/target/linux/generic/backport-6.12/410-03-v6.14-mtd-nand-Add-qpic_common-API-file.patch @@ -0,0 +1,2436 @@ +From fdf3ee5c6e5278dab4f60b998b47ed2d510bf80f Mon Sep 17 00:00:00 2001 +From: Md Sadre Alam +Date: Wed, 20 Nov 2024 14:45:02 +0530 +Subject: [PATCH 3/4] mtd: nand: Add qpic_common API file + +Add qpic_common.c file which hold all the common +qpic APIs which will be used by both qpic raw nand +driver and qpic spi nand driver. + +Signed-off-by: Md Sadre Alam +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/Makefile | 2 +- + drivers/mtd/nand/qpic_common.c | 759 ++++++++++++++++++ + drivers/mtd/nand/raw/qcom_nandc.c | 1092 +------------------------- + include/linux/mtd/nand-qpic-common.h | 468 +++++++++++ + 4 files changed, 1240 insertions(+), 1081 deletions(-) + create mode 100644 drivers/mtd/nand/qpic_common.c + create mode 100644 include/linux/mtd/nand-qpic-common.h + +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -3,7 +3,7 @@ + nandcore-objs := core.o bbt.o + obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o + obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o +- ++obj-$(CONFIG_MTD_NAND_QCOM) += qpic_common.o + obj-y += onenand/ + obj-y += raw/ + obj-y += spi/ +--- /dev/null ++++ b/drivers/mtd/nand/qpic_common.c +@@ -0,0 +1,759 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2016, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * qcom_free_bam_transaction() - Frees the BAM transaction memory ++ * @nandc: qpic nand controller ++ * ++ * This function frees the bam transaction memory ++ */ ++void qcom_free_bam_transaction(struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ kfree(bam_txn); ++} ++EXPORT_SYMBOL(qcom_free_bam_transaction); ++ ++/** ++ * qcom_alloc_bam_transaction() - allocate BAM transaction ++ * @nandc: qpic nand controller ++ * ++ * This function will allocate and initialize the BAM transaction structure ++ */ ++struct bam_transaction * ++qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn; ++ size_t bam_txn_size; ++ unsigned int num_cw = nandc->max_cwperpage; ++ void *bam_txn_buf; ++ ++ bam_txn_size = ++ sizeof(*bam_txn) + num_cw * ++ ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) + ++ (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + ++ (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); ++ ++ bam_txn_buf = kzalloc(bam_txn_size, GFP_KERNEL); ++ if (!bam_txn_buf) ++ return NULL; ++ ++ bam_txn = bam_txn_buf; ++ bam_txn_buf += sizeof(*bam_txn); ++ ++ bam_txn->bam_ce = bam_txn_buf; ++ bam_txn_buf += ++ sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw; ++ ++ bam_txn->cmd_sgl = bam_txn_buf; ++ bam_txn_buf += ++ sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; ++ ++ bam_txn->data_sgl = bam_txn_buf; ++ ++ init_completion(&bam_txn->txn_done); ++ ++ return bam_txn; ++} ++EXPORT_SYMBOL(qcom_alloc_bam_transaction); ++ ++/** ++ * qcom_clear_bam_transaction() - Clears the BAM transaction ++ * @nandc: qpic nand controller ++ * ++ * This function will clear the BAM transaction indexes. ++ */ ++void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ if (!nandc->props->supports_bam) ++ return; ++ ++ memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8); ++ bam_txn->last_data_desc = NULL; ++ ++ sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * ++ QPIC_PER_CW_CMD_SGL); ++ sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * ++ QPIC_PER_CW_DATA_SGL); ++ ++ reinit_completion(&bam_txn->txn_done); ++} ++EXPORT_SYMBOL(qcom_clear_bam_transaction); ++ ++/** ++ * qcom_qpic_bam_dma_done() - Callback for DMA descriptor completion ++ * @data: data pointer ++ * ++ * This function is a callback for DMA descriptor completion ++ */ ++void qcom_qpic_bam_dma_done(void *data) ++{ ++ struct bam_transaction *bam_txn = data; ++ ++ complete(&bam_txn->txn_done); ++} ++EXPORT_SYMBOL(qcom_qpic_bam_dma_done); ++ ++/** ++ * qcom_nandc_dev_to_mem() - Check for dma sync for cpu or device ++ * @nandc: qpic nand controller ++ * @is_cpu: cpu or Device ++ * ++ * This function will check for dma sync for cpu or device ++ */ ++inline void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu) ++{ ++ if (!nandc->props->supports_bam) ++ return; ++ ++ if (is_cpu) ++ dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++ else ++ dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++} ++EXPORT_SYMBOL(qcom_nandc_dev_to_mem); ++ ++/** ++ * qcom_prepare_bam_async_desc() - Prepare DMA descriptor ++ * @nandc: qpic nand controller ++ * @chan: dma channel ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function maps the scatter gather list for DMA transfer and forms the ++ * DMA descriptor for BAM.This descriptor will be added in the NAND DMA ++ * descriptor queue which will be submitted to DMA engine. ++ */ ++int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc, ++ struct dma_chan *chan, unsigned long flags) ++{ ++ struct desc_info *desc; ++ struct scatterlist *sgl; ++ unsigned int sgl_cnt; ++ int ret; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ enum dma_transfer_direction dir_eng; ++ struct dma_async_tx_descriptor *dma_desc; ++ ++ desc = kzalloc(sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ if (chan == nandc->cmd_chan) { ++ sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; ++ sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; ++ bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; ++ dir_eng = DMA_MEM_TO_DEV; ++ desc->dir = DMA_TO_DEVICE; ++ } else if (chan == nandc->tx_chan) { ++ sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; ++ sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; ++ bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; ++ dir_eng = DMA_MEM_TO_DEV; ++ desc->dir = DMA_TO_DEVICE; ++ } else { ++ sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; ++ sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; ++ bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; ++ dir_eng = DMA_DEV_TO_MEM; ++ desc->dir = DMA_FROM_DEVICE; ++ } ++ ++ sg_mark_end(sgl + sgl_cnt - 1); ++ ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); ++ if (ret == 0) { ++ dev_err(nandc->dev, "failure in mapping desc\n"); ++ kfree(desc); ++ return -ENOMEM; ++ } ++ ++ desc->sgl_cnt = sgl_cnt; ++ desc->bam_sgl = sgl; ++ ++ dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, ++ flags); ++ ++ if (!dma_desc) { ++ dev_err(nandc->dev, "failure in prep desc\n"); ++ dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); ++ kfree(desc); ++ return -EINVAL; ++ } ++ ++ desc->dma_desc = dma_desc; ++ ++ /* update last data/command descriptor */ ++ if (chan == nandc->cmd_chan) ++ bam_txn->last_cmd_desc = dma_desc; ++ else ++ bam_txn->last_data_desc = dma_desc; ++ ++ list_add_tail(&desc->node, &nandc->desc_list); ++ ++ return 0; ++} ++EXPORT_SYMBOL(qcom_prepare_bam_async_desc); ++ ++/** ++ * qcom_prep_bam_dma_desc_cmd() - Prepares the command descriptor for BAM DMA ++ * @nandc: qpic nand controller ++ * @read: read or write type ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: DMA transaction size in bytes ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares the command descriptor for BAM DMA ++ * which will be used for NAND register reads and writes. ++ */ ++int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, ++ int size, unsigned int flags) ++{ ++ int bam_ce_size; ++ int i, ret; ++ struct bam_cmd_element *bam_ce_buffer; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; ++ ++ /* fill the command desc */ ++ for (i = 0; i < size; i++) { ++ if (read) ++ bam_prep_ce(&bam_ce_buffer[i], ++ nandc_reg_phys(nandc, reg_off + 4 * i), ++ BAM_READ_COMMAND, ++ reg_buf_dma_addr(nandc, ++ (__le32 *)vaddr + i)); ++ else ++ bam_prep_ce_le32(&bam_ce_buffer[i], ++ nandc_reg_phys(nandc, reg_off + 4 * i), ++ BAM_WRITE_COMMAND, ++ *((__le32 *)vaddr + i)); ++ } ++ ++ bam_txn->bam_ce_pos += size; ++ ++ /* use the separate sgl after this command */ ++ if (flags & NAND_BAM_NEXT_SGL) { ++ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; ++ bam_ce_size = (bam_txn->bam_ce_pos - ++ bam_txn->bam_ce_start) * ++ sizeof(struct bam_cmd_element); ++ sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], ++ bam_ce_buffer, bam_ce_size); ++ bam_txn->cmd_sgl_pos++; ++ bam_txn->bam_ce_start = bam_txn->bam_ce_pos; ++ ++ if (flags & NAND_BAM_NWD) { ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, ++ DMA_PREP_FENCE | DMA_PREP_CMD); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(qcom_prep_bam_dma_desc_cmd); ++ ++/** ++ * qcom_prep_bam_dma_desc_data() - Prepares the data descriptor for BAM DMA ++ * @nandc: qpic nand controller ++ * @read: read or write type ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: DMA transaction size in bytes ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares the data descriptor for BAM DMA which ++ * will be used for NAND data reads and writes. ++ */ ++int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, ++ const void *vaddr, int size, unsigned int flags) ++{ ++ int ret; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ if (read) { ++ sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], ++ vaddr, size); ++ bam_txn->rx_sgl_pos++; ++ } else { ++ sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], ++ vaddr, size); ++ bam_txn->tx_sgl_pos++; ++ ++ /* ++ * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag ++ * is not set, form the DMA descriptor ++ */ ++ if (!(flags & NAND_BAM_NO_EOT)) { ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, ++ DMA_PREP_INTERRUPT); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(qcom_prep_bam_dma_desc_data); ++ ++/** ++ * qcom_prep_adm_dma_desc() - Prepare descriptor for adma ++ * @nandc: qpic nand controller ++ * @read: read or write type ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: adm dma transaction size in bytes ++ * @flow_control: flow controller ++ * ++ * This function will prepare descriptor for adma ++ */ ++int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, int size, ++ bool flow_control) ++{ ++ struct qcom_adm_peripheral_config periph_conf = {}; ++ struct dma_async_tx_descriptor *dma_desc; ++ struct dma_slave_config slave_conf = {0}; ++ enum dma_transfer_direction dir_eng; ++ struct desc_info *desc; ++ struct scatterlist *sgl; ++ int ret; ++ ++ desc = kzalloc(sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ sgl = &desc->adm_sgl; ++ ++ sg_init_one(sgl, vaddr, size); ++ ++ if (read) { ++ dir_eng = DMA_DEV_TO_MEM; ++ desc->dir = DMA_FROM_DEVICE; ++ } else { ++ dir_eng = DMA_MEM_TO_DEV; ++ desc->dir = DMA_TO_DEVICE; ++ } ++ ++ ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir); ++ if (!ret) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ slave_conf.device_fc = flow_control; ++ if (read) { ++ slave_conf.src_maxburst = 16; ++ slave_conf.src_addr = nandc->base_dma + reg_off; ++ if (nandc->data_crci) { ++ periph_conf.crci = nandc->data_crci; ++ slave_conf.peripheral_config = &periph_conf; ++ slave_conf.peripheral_size = sizeof(periph_conf); ++ } ++ } else { ++ slave_conf.dst_maxburst = 16; ++ slave_conf.dst_addr = nandc->base_dma + reg_off; ++ if (nandc->cmd_crci) { ++ periph_conf.crci = nandc->cmd_crci; ++ slave_conf.peripheral_config = &periph_conf; ++ slave_conf.peripheral_size = sizeof(periph_conf); ++ } ++ } ++ ++ ret = dmaengine_slave_config(nandc->chan, &slave_conf); ++ if (ret) { ++ dev_err(nandc->dev, "failed to configure dma channel\n"); ++ goto err; ++ } ++ ++ dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0); ++ if (!dma_desc) { ++ dev_err(nandc->dev, "failed to prepare desc\n"); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ desc->dma_desc = dma_desc; ++ ++ list_add_tail(&desc->node, &nandc->desc_list); ++ ++ return 0; ++err: ++ kfree(desc); ++ ++ return ret; ++} ++EXPORT_SYMBOL(qcom_prep_adm_dma_desc); ++ ++/** ++ * qcom_read_reg_dma() - read a given number of registers to the reg_read_buf pointer ++ * @nandc: qpic nand controller ++ * @first: offset of the first register in the contiguous block ++ * @num_regs: number of registers to read ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares a descriptor to read a given number of ++ * contiguous registers to the reg_read_buf pointer. ++ */ ++int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first, ++ int num_regs, unsigned int flags) ++{ ++ bool flow_control = false; ++ void *vaddr; ++ ++ vaddr = nandc->reg_read_buf + nandc->reg_read_pos; ++ nandc->reg_read_pos += num_regs; ++ ++ if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) ++ first = dev_cmd_reg_addr(nandc, first); ++ ++ if (nandc->props->supports_bam) ++ return qcom_prep_bam_dma_desc_cmd(nandc, true, first, vaddr, ++ num_regs, flags); ++ ++ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) ++ flow_control = true; ++ ++ return qcom_prep_adm_dma_desc(nandc, true, first, vaddr, ++ num_regs * sizeof(u32), flow_control); ++} ++EXPORT_SYMBOL(qcom_read_reg_dma); ++ ++/** ++ * qcom_write_reg_dma() - write a given number of registers ++ * @nandc: qpic nand controller ++ * @vaddr: contiguous memory from where register value will ++ * be written ++ * @first: offset of the first register in the contiguous block ++ * @num_regs: number of registers to write ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares a descriptor to write a given number of ++ * contiguous registers ++ */ ++int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, ++ int first, int num_regs, unsigned int flags) ++{ ++ bool flow_control = false; ++ ++ if (first == NAND_EXEC_CMD) ++ flags |= NAND_BAM_NWD; ++ ++ if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) ++ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); ++ ++ if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) ++ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); ++ ++ if (nandc->props->supports_bam) ++ return qcom_prep_bam_dma_desc_cmd(nandc, false, first, vaddr, ++ num_regs, flags); ++ ++ if (first == NAND_FLASH_CMD) ++ flow_control = true; ++ ++ return qcom_prep_adm_dma_desc(nandc, false, first, vaddr, ++ num_regs * sizeof(u32), flow_control); ++} ++EXPORT_SYMBOL(qcom_write_reg_dma); ++ ++/** ++ * qcom_read_data_dma() - transfer data ++ * @nandc: qpic nand controller ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: DMA transaction size in bytes ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares a DMA descriptor to transfer data from the ++ * controller's internal buffer to the buffer 'vaddr' ++ */ ++int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off, ++ const u8 *vaddr, int size, unsigned int flags) ++{ ++ if (nandc->props->supports_bam) ++ return qcom_prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); ++ ++ return qcom_prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); ++} ++EXPORT_SYMBOL(qcom_read_data_dma); ++ ++/** ++ * qcom_write_data_dma() - transfer data ++ * @nandc: qpic nand controller ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to read from ++ * @size: DMA transaction size in bytes ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares a DMA descriptor to transfer data from ++ * 'vaddr' to the controller's internal buffer ++ */ ++int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off, ++ const u8 *vaddr, int size, unsigned int flags) ++{ ++ if (nandc->props->supports_bam) ++ return qcom_prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); ++ ++ return qcom_prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); ++} ++EXPORT_SYMBOL(qcom_write_data_dma); ++ ++/** ++ * qcom_submit_descs() - submit dma descriptor ++ * @nandc: qpic nand controller ++ * ++ * This function will submit all the prepared dma descriptor ++ * cmd or data descriptor ++ */ ++int qcom_submit_descs(struct qcom_nand_controller *nandc) ++{ ++ struct desc_info *desc, *n; ++ dma_cookie_t cookie = 0; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ int ret = 0; ++ ++ if (nandc->props->supports_bam) { ++ if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->rx_chan, 0); ++ if (ret) ++ goto err_unmap_free_desc; ++ } ++ ++ if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, ++ DMA_PREP_INTERRUPT); ++ if (ret) ++ goto err_unmap_free_desc; ++ } ++ ++ if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, ++ DMA_PREP_CMD); ++ if (ret) ++ goto err_unmap_free_desc; ++ } ++ } ++ ++ list_for_each_entry(desc, &nandc->desc_list, node) ++ cookie = dmaengine_submit(desc->dma_desc); ++ ++ if (nandc->props->supports_bam) { ++ bam_txn->last_cmd_desc->callback = qcom_qpic_bam_dma_done; ++ bam_txn->last_cmd_desc->callback_param = bam_txn; ++ ++ dma_async_issue_pending(nandc->tx_chan); ++ dma_async_issue_pending(nandc->rx_chan); ++ dma_async_issue_pending(nandc->cmd_chan); ++ ++ if (!wait_for_completion_timeout(&bam_txn->txn_done, ++ QPIC_NAND_COMPLETION_TIMEOUT)) ++ ret = -ETIMEDOUT; ++ } else { ++ if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) ++ ret = -ETIMEDOUT; ++ } ++ ++err_unmap_free_desc: ++ /* ++ * Unmap the dma sg_list and free the desc allocated by both ++ * qcom_prepare_bam_async_desc() and qcom_prep_adm_dma_desc() functions. ++ */ ++ list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { ++ list_del(&desc->node); ++ ++ if (nandc->props->supports_bam) ++ dma_unmap_sg(nandc->dev, desc->bam_sgl, ++ desc->sgl_cnt, desc->dir); ++ else ++ dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, ++ desc->dir); ++ ++ kfree(desc); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(qcom_submit_descs); ++ ++/** ++ * qcom_clear_read_regs() - reset the read register buffer ++ * @nandc: qpic nand controller ++ * ++ * This function reset the register read buffer for next NAND operation ++ */ ++void qcom_clear_read_regs(struct qcom_nand_controller *nandc) ++{ ++ nandc->reg_read_pos = 0; ++ qcom_nandc_dev_to_mem(nandc, false); ++} ++EXPORT_SYMBOL(qcom_clear_read_regs); ++ ++/** ++ * qcom_nandc_unalloc() - unallocate qpic nand controller ++ * @nandc: qpic nand controller ++ * ++ * This function will unallocate memory alloacted for qpic nand controller ++ */ ++void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) ++{ ++ if (nandc->props->supports_bam) { ++ if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) ++ dma_unmap_single(nandc->dev, nandc->reg_read_dma, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++ ++ if (nandc->tx_chan) ++ dma_release_channel(nandc->tx_chan); ++ ++ if (nandc->rx_chan) ++ dma_release_channel(nandc->rx_chan); ++ ++ if (nandc->cmd_chan) ++ dma_release_channel(nandc->cmd_chan); ++ } else { ++ if (nandc->chan) ++ dma_release_channel(nandc->chan); ++ } ++} ++EXPORT_SYMBOL(qcom_nandc_unalloc); ++ ++/** ++ * qcom_nandc_alloc() - Allocate qpic nand controller ++ * @nandc: qpic nand controller ++ * ++ * This function will allocate memory for qpic nand controller ++ */ ++int qcom_nandc_alloc(struct qcom_nand_controller *nandc) ++{ ++ int ret; ++ ++ ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32)); ++ if (ret) { ++ dev_err(nandc->dev, "failed to set DMA mask\n"); ++ return ret; ++ } ++ ++ /* ++ * we use the internal buffer for reading ONFI params, reading small ++ * data like ID and status, and preforming read-copy-write operations ++ * when writing to a codeword partially. 532 is the maximum possible ++ * size of a codeword for our nand controller ++ */ ++ nandc->buf_size = 532; ++ ++ nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, GFP_KERNEL); ++ if (!nandc->data_buffer) ++ return -ENOMEM; ++ ++ nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), GFP_KERNEL); ++ if (!nandc->regs) ++ return -ENOMEM; ++ ++ nandc->reg_read_buf = devm_kcalloc(nandc->dev, MAX_REG_RD, ++ sizeof(*nandc->reg_read_buf), ++ GFP_KERNEL); ++ if (!nandc->reg_read_buf) ++ return -ENOMEM; ++ ++ if (nandc->props->supports_bam) { ++ nandc->reg_read_dma = ++ dma_map_single(nandc->dev, nandc->reg_read_buf, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++ if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { ++ dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); ++ return -EIO; ++ } ++ ++ nandc->tx_chan = dma_request_chan(nandc->dev, "tx"); ++ if (IS_ERR(nandc->tx_chan)) { ++ ret = PTR_ERR(nandc->tx_chan); ++ nandc->tx_chan = NULL; ++ dev_err_probe(nandc->dev, ret, ++ "tx DMA channel request failed\n"); ++ goto unalloc; ++ } ++ ++ nandc->rx_chan = dma_request_chan(nandc->dev, "rx"); ++ if (IS_ERR(nandc->rx_chan)) { ++ ret = PTR_ERR(nandc->rx_chan); ++ nandc->rx_chan = NULL; ++ dev_err_probe(nandc->dev, ret, ++ "rx DMA channel request failed\n"); ++ goto unalloc; ++ } ++ ++ nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd"); ++ if (IS_ERR(nandc->cmd_chan)) { ++ ret = PTR_ERR(nandc->cmd_chan); ++ nandc->cmd_chan = NULL; ++ dev_err_probe(nandc->dev, ret, ++ "cmd DMA channel request failed\n"); ++ goto unalloc; ++ } ++ ++ /* ++ * Initially allocate BAM transaction to read ONFI param page. ++ * After detecting all the devices, this BAM transaction will ++ * be freed and the next BAM transaction will be allocated with ++ * maximum codeword size ++ */ ++ nandc->max_cwperpage = 1; ++ nandc->bam_txn = qcom_alloc_bam_transaction(nandc); ++ if (!nandc->bam_txn) { ++ dev_err(nandc->dev, ++ "failed to allocate bam transaction\n"); ++ ret = -ENOMEM; ++ goto unalloc; ++ } ++ } else { ++ nandc->chan = dma_request_chan(nandc->dev, "rxtx"); ++ if (IS_ERR(nandc->chan)) { ++ ret = PTR_ERR(nandc->chan); ++ nandc->chan = NULL; ++ dev_err_probe(nandc->dev, ret, ++ "rxtx DMA channel request failed\n"); ++ return ret; ++ } ++ } ++ ++ INIT_LIST_HEAD(&nandc->desc_list); ++ INIT_LIST_HEAD(&nandc->host_list); ++ ++ return 0; ++unalloc: ++ qcom_nandc_unalloc(nandc); ++ return ret; ++} ++EXPORT_SYMBOL(qcom_nandc_alloc); ++ ++MODULE_DESCRIPTION("QPIC controller common api"); ++MODULE_LICENSE("GPL"); +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -15,417 +15,7 @@ + #include + #include + #include +- +-/* NANDc reg offsets */ +-#define NAND_FLASH_CMD 0x00 +-#define NAND_ADDR0 0x04 +-#define NAND_ADDR1 0x08 +-#define NAND_FLASH_CHIP_SELECT 0x0c +-#define NAND_EXEC_CMD 0x10 +-#define NAND_FLASH_STATUS 0x14 +-#define NAND_BUFFER_STATUS 0x18 +-#define NAND_DEV0_CFG0 0x20 +-#define NAND_DEV0_CFG1 0x24 +-#define NAND_DEV0_ECC_CFG 0x28 +-#define NAND_AUTO_STATUS_EN 0x2c +-#define NAND_DEV1_CFG0 0x30 +-#define NAND_DEV1_CFG1 0x34 +-#define NAND_READ_ID 0x40 +-#define NAND_READ_STATUS 0x44 +-#define NAND_DEV_CMD0 0xa0 +-#define NAND_DEV_CMD1 0xa4 +-#define NAND_DEV_CMD2 0xa8 +-#define NAND_DEV_CMD_VLD 0xac +-#define SFLASHC_BURST_CFG 0xe0 +-#define NAND_ERASED_CW_DETECT_CFG 0xe8 +-#define NAND_ERASED_CW_DETECT_STATUS 0xec +-#define NAND_EBI2_ECC_BUF_CFG 0xf0 +-#define FLASH_BUF_ACC 0x100 +- +-#define NAND_CTRL 0xf00 +-#define NAND_VERSION 0xf08 +-#define NAND_READ_LOCATION_0 0xf20 +-#define NAND_READ_LOCATION_1 0xf24 +-#define NAND_READ_LOCATION_2 0xf28 +-#define NAND_READ_LOCATION_3 0xf2c +-#define NAND_READ_LOCATION_LAST_CW_0 0xf40 +-#define NAND_READ_LOCATION_LAST_CW_1 0xf44 +-#define NAND_READ_LOCATION_LAST_CW_2 0xf48 +-#define NAND_READ_LOCATION_LAST_CW_3 0xf4c +- +-/* dummy register offsets, used by qcom_write_reg_dma */ +-#define NAND_DEV_CMD1_RESTORE 0xdead +-#define NAND_DEV_CMD_VLD_RESTORE 0xbeef +- +-/* NAND_FLASH_CMD bits */ +-#define PAGE_ACC BIT(4) +-#define LAST_PAGE BIT(5) +- +-/* NAND_FLASH_CHIP_SELECT bits */ +-#define NAND_DEV_SEL 0 +-#define DM_EN BIT(2) +- +-/* NAND_FLASH_STATUS bits */ +-#define FS_OP_ERR BIT(4) +-#define FS_READY_BSY_N BIT(5) +-#define FS_MPU_ERR BIT(8) +-#define FS_DEVICE_STS_ERR BIT(16) +-#define FS_DEVICE_WP BIT(23) +- +-/* NAND_BUFFER_STATUS bits */ +-#define BS_UNCORRECTABLE_BIT BIT(8) +-#define BS_CORRECTABLE_ERR_MSK 0x1f +- +-/* NAND_DEVn_CFG0 bits */ +-#define DISABLE_STATUS_AFTER_WRITE 4 +-#define CW_PER_PAGE 6 +-#define UD_SIZE_BYTES 9 +-#define UD_SIZE_BYTES_MASK GENMASK(18, 9) +-#define ECC_PARITY_SIZE_BYTES_RS 19 +-#define SPARE_SIZE_BYTES 23 +-#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23) +-#define NUM_ADDR_CYCLES 27 +-#define STATUS_BFR_READ 30 +-#define SET_RD_MODE_AFTER_STATUS 31 +- +-/* NAND_DEVn_CFG0 bits */ +-#define DEV0_CFG1_ECC_DISABLE 0 +-#define WIDE_FLASH 1 +-#define NAND_RECOVERY_CYCLES 2 +-#define CS_ACTIVE_BSY 5 +-#define BAD_BLOCK_BYTE_NUM 6 +-#define BAD_BLOCK_IN_SPARE_AREA 16 +-#define WR_RD_BSY_GAP 17 +-#define ENABLE_BCH_ECC 27 +- +-/* NAND_DEV0_ECC_CFG bits */ +-#define ECC_CFG_ECC_DISABLE 0 +-#define ECC_SW_RESET 1 +-#define ECC_MODE 4 +-#define ECC_PARITY_SIZE_BYTES_BCH 8 +-#define ECC_NUM_DATA_BYTES 16 +-#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16) +-#define ECC_FORCE_CLK_OPEN 30 +- +-/* NAND_DEV_CMD1 bits */ +-#define READ_ADDR 0 +- +-/* NAND_DEV_CMD_VLD bits */ +-#define READ_START_VLD BIT(0) +-#define READ_STOP_VLD BIT(1) +-#define WRITE_START_VLD BIT(2) +-#define ERASE_START_VLD BIT(3) +-#define SEQ_READ_START_VLD BIT(4) +- +-/* NAND_EBI2_ECC_BUF_CFG bits */ +-#define NUM_STEPS 0 +- +-/* NAND_ERASED_CW_DETECT_CFG bits */ +-#define ERASED_CW_ECC_MASK 1 +-#define AUTO_DETECT_RES 0 +-#define MASK_ECC BIT(ERASED_CW_ECC_MASK) +-#define RESET_ERASED_DET BIT(AUTO_DETECT_RES) +-#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) +-#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) +-#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) +- +-/* NAND_ERASED_CW_DETECT_STATUS bits */ +-#define PAGE_ALL_ERASED BIT(7) +-#define CODEWORD_ALL_ERASED BIT(6) +-#define PAGE_ERASED BIT(5) +-#define CODEWORD_ERASED BIT(4) +-#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) +-#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) +- +-/* NAND_READ_LOCATION_n bits */ +-#define READ_LOCATION_OFFSET 0 +-#define READ_LOCATION_SIZE 16 +-#define READ_LOCATION_LAST 31 +- +-/* Version Mask */ +-#define NAND_VERSION_MAJOR_MASK 0xf0000000 +-#define NAND_VERSION_MAJOR_SHIFT 28 +-#define NAND_VERSION_MINOR_MASK 0x0fff0000 +-#define NAND_VERSION_MINOR_SHIFT 16 +- +-/* NAND OP_CMDs */ +-#define OP_PAGE_READ 0x2 +-#define OP_PAGE_READ_WITH_ECC 0x3 +-#define OP_PAGE_READ_WITH_ECC_SPARE 0x4 +-#define OP_PAGE_READ_ONFI_READ 0x5 +-#define OP_PROGRAM_PAGE 0x6 +-#define OP_PAGE_PROGRAM_WITH_ECC 0x7 +-#define OP_PROGRAM_PAGE_SPARE 0x9 +-#define OP_BLOCK_ERASE 0xa +-#define OP_CHECK_STATUS 0xc +-#define OP_FETCH_ID 0xb +-#define OP_RESET_DEVICE 0xd +- +-/* Default Value for NAND_DEV_CMD_VLD */ +-#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ +- ERASE_START_VLD | SEQ_READ_START_VLD) +- +-/* NAND_CTRL bits */ +-#define BAM_MODE_EN BIT(0) +- +-/* +- * the NAND controller performs reads/writes with ECC in 516 byte chunks. +- * the driver calls the chunks 'step' or 'codeword' interchangeably +- */ +-#define NANDC_STEP_SIZE 512 +- +-/* +- * the largest page size we support is 8K, this will have 16 steps/codewords +- * of 512 bytes each +- */ +-#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) +- +-/* we read at most 3 registers per codeword scan */ +-#define MAX_REG_RD (3 * MAX_NUM_STEPS) +- +-/* ECC modes supported by the controller */ +-#define ECC_NONE BIT(0) +-#define ECC_RS_4BIT BIT(1) +-#define ECC_BCH_4BIT BIT(2) +-#define ECC_BCH_8BIT BIT(3) +- +-/* +- * Returns the actual register address for all NAND_DEV_ registers +- * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) +- */ +-#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) +- +-/* Returns the NAND register physical address */ +-#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset)) +- +-/* Returns the dma address for reg read buffer */ +-#define reg_buf_dma_addr(chip, vaddr) \ +- ((chip)->reg_read_dma + \ +- ((u8 *)(vaddr) - (u8 *)(chip)->reg_read_buf)) +- +-#define QPIC_PER_CW_CMD_ELEMENTS 32 +-#define QPIC_PER_CW_CMD_SGL 32 +-#define QPIC_PER_CW_DATA_SGL 8 +- +-#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000) +- +-/* +- * Flags used in DMA descriptor preparation helper functions +- * (i.e. qcom_read_reg_dma/qcom_write_reg_dma/qcom_read_data_dma/qcom_write_data_dma) +- */ +-/* Don't set the EOT in current tx BAM sgl */ +-#define NAND_BAM_NO_EOT BIT(0) +-/* Set the NWD flag in current BAM sgl */ +-#define NAND_BAM_NWD BIT(1) +-/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ +-#define NAND_BAM_NEXT_SGL BIT(2) +-/* +- * Erased codeword status is being used two times in single transfer so this +- * flag will determine the current value of erased codeword status register +- */ +-#define NAND_ERASED_CW_SET BIT(4) +- +-#define MAX_ADDRESS_CYCLE 5 +- +-/* +- * This data type corresponds to the BAM transaction which will be used for all +- * NAND transfers. +- * @bam_ce - the array of BAM command elements +- * @cmd_sgl - sgl for NAND BAM command pipe +- * @data_sgl - sgl for NAND BAM consumer/producer pipe +- * @last_data_desc - last DMA desc in data channel (tx/rx). +- * @last_cmd_desc - last DMA desc in command channel. +- * @txn_done - completion for NAND transfer. +- * @bam_ce_pos - the index in bam_ce which is available for next sgl +- * @bam_ce_start - the index in bam_ce which marks the start position ce +- * for current sgl. It will be used for size calculation +- * for current sgl +- * @cmd_sgl_pos - current index in command sgl. +- * @cmd_sgl_start - start index in command sgl. +- * @tx_sgl_pos - current index in data sgl for tx. +- * @tx_sgl_start - start index in data sgl for tx. +- * @rx_sgl_pos - current index in data sgl for rx. +- * @rx_sgl_start - start index in data sgl for rx. +- */ +-struct bam_transaction { +- struct bam_cmd_element *bam_ce; +- struct scatterlist *cmd_sgl; +- struct scatterlist *data_sgl; +- struct dma_async_tx_descriptor *last_data_desc; +- struct dma_async_tx_descriptor *last_cmd_desc; +- struct completion txn_done; +- u32 bam_ce_pos; +- u32 bam_ce_start; +- u32 cmd_sgl_pos; +- u32 cmd_sgl_start; +- u32 tx_sgl_pos; +- u32 tx_sgl_start; +- u32 rx_sgl_pos; +- u32 rx_sgl_start; +-}; +- +-/* +- * This data type corresponds to the nand dma descriptor +- * @dma_desc - low level DMA engine descriptor +- * @list - list for desc_info +- * +- * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by +- * ADM +- * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM +- * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM +- * @dir - DMA transfer direction +- */ +-struct desc_info { +- struct dma_async_tx_descriptor *dma_desc; +- struct list_head node; +- +- union { +- struct scatterlist adm_sgl; +- struct { +- struct scatterlist *bam_sgl; +- int sgl_cnt; +- }; +- }; +- enum dma_data_direction dir; +-}; +- +-/* +- * holds the current register values that we want to write. acts as a contiguous +- * chunk of memory which we use to write the controller registers through DMA. +- */ +-struct nandc_regs { +- __le32 cmd; +- __le32 addr0; +- __le32 addr1; +- __le32 chip_sel; +- __le32 exec; +- +- __le32 cfg0; +- __le32 cfg1; +- __le32 ecc_bch_cfg; +- +- __le32 clrflashstatus; +- __le32 clrreadstatus; +- +- __le32 cmd1; +- __le32 vld; +- +- __le32 orig_cmd1; +- __le32 orig_vld; +- +- __le32 ecc_buf_cfg; +- __le32 read_location0; +- __le32 read_location1; +- __le32 read_location2; +- __le32 read_location3; +- __le32 read_location_last0; +- __le32 read_location_last1; +- __le32 read_location_last2; +- __le32 read_location_last3; +- +- __le32 erased_cw_detect_cfg_clr; +- __le32 erased_cw_detect_cfg_set; +-}; +- +-/* +- * NAND controller data struct +- * +- * @dev: parent device +- * +- * @base: MMIO base +- * +- * @core_clk: controller clock +- * @aon_clk: another controller clock +- * +- * @regs: a contiguous chunk of memory for DMA register +- * writes. contains the register values to be +- * written to controller +- * +- * @props: properties of current NAND controller, +- * initialized via DT match data +- * +- * @controller: base controller structure +- * @host_list: list containing all the chips attached to the +- * controller +- * +- * @chan: dma channel +- * @cmd_crci: ADM DMA CRCI for command flow control +- * @data_crci: ADM DMA CRCI for data flow control +- * +- * @desc_list: DMA descriptor list (list of desc_infos) +- * +- * @data_buffer: our local DMA buffer for page read/writes, +- * used when we can't use the buffer provided +- * by upper layers directly +- * @reg_read_buf: local buffer for reading back registers via DMA +- * +- * @base_phys: physical base address of controller registers +- * @base_dma: dma base address of controller registers +- * @reg_read_dma: contains dma address for register read buffer +- * +- * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf +- * functions +- * @max_cwperpage: maximum QPIC codewords required. calculated +- * from all connected NAND devices pagesize +- * +- * @reg_read_pos: marker for data read in reg_read_buf +- * +- * @cmd1/vld: some fixed controller register values +- * +- * @exec_opwrite: flag to select correct number of code word +- * while reading status +- */ +-struct qcom_nand_controller { +- struct device *dev; +- +- void __iomem *base; +- +- struct clk *core_clk; +- struct clk *aon_clk; +- +- struct nandc_regs *regs; +- struct bam_transaction *bam_txn; +- +- const struct qcom_nandc_props *props; +- +- struct nand_controller controller; +- struct list_head host_list; +- +- union { +- /* will be used only by QPIC for BAM DMA */ +- struct { +- struct dma_chan *tx_chan; +- struct dma_chan *rx_chan; +- struct dma_chan *cmd_chan; +- }; +- +- /* will be used only by EBI2 for ADM DMA */ +- struct { +- struct dma_chan *chan; +- unsigned int cmd_crci; +- unsigned int data_crci; +- }; +- }; +- +- struct list_head desc_list; +- +- u8 *data_buffer; +- __le32 *reg_read_buf; +- +- phys_addr_t base_phys; +- dma_addr_t base_dma; +- dma_addr_t reg_read_dma; +- +- int buf_size; +- int buf_count; +- int buf_start; +- unsigned int max_cwperpage; +- +- int reg_read_pos; +- +- u32 cmd1, vld; +- bool exec_opwrite; +-}; ++#include + + /* + * NAND special boot partitions +@@ -530,97 +120,6 @@ struct qcom_nand_host { + bool bch_enabled; + }; + +-/* +- * This data type corresponds to the NAND controller properties which varies +- * among different NAND controllers. +- * @ecc_modes - ecc mode for NAND +- * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset +- * @supports_bam - whether NAND controller is using Bus Access Manager (BAM) +- * @nandc_part_of_qpic - whether NAND controller is part of qpic IP +- * @qpic_version2 - flag to indicate QPIC IP version 2 +- * @use_codeword_fixup - whether NAND has different layout for boot partitions +- */ +-struct qcom_nandc_props { +- u32 ecc_modes; +- u32 dev_cmd_reg_start; +- bool supports_bam; +- bool nandc_part_of_qpic; +- bool qpic_version2; +- bool use_codeword_fixup; +-}; +- +-/* Frees the BAM transaction memory */ +-static void qcom_free_bam_transaction(struct qcom_nand_controller *nandc) +-{ +- struct bam_transaction *bam_txn = nandc->bam_txn; +- +- devm_kfree(nandc->dev, bam_txn); +-} +- +-/* Allocates and Initializes the BAM transaction */ +-static struct bam_transaction * +-qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc) +-{ +- struct bam_transaction *bam_txn; +- size_t bam_txn_size; +- unsigned int num_cw = nandc->max_cwperpage; +- void *bam_txn_buf; +- +- bam_txn_size = +- sizeof(*bam_txn) + num_cw * +- ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) + +- (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + +- (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); +- +- bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL); +- if (!bam_txn_buf) +- return NULL; +- +- bam_txn = bam_txn_buf; +- bam_txn_buf += sizeof(*bam_txn); +- +- bam_txn->bam_ce = bam_txn_buf; +- bam_txn_buf += +- sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw; +- +- bam_txn->cmd_sgl = bam_txn_buf; +- bam_txn_buf += +- sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; +- +- bam_txn->data_sgl = bam_txn_buf; +- +- init_completion(&bam_txn->txn_done); +- +- return bam_txn; +-} +- +-/* Clears the BAM transaction indexes */ +-static void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc) +-{ +- struct bam_transaction *bam_txn = nandc->bam_txn; +- +- if (!nandc->props->supports_bam) +- return; +- +- memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8); +- bam_txn->last_data_desc = NULL; +- +- sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * +- QPIC_PER_CW_CMD_SGL); +- sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * +- QPIC_PER_CW_DATA_SGL); +- +- reinit_completion(&bam_txn->txn_done); +-} +- +-/* Callback for DMA descriptor completion */ +-static void qcom_qpic_bam_dma_done(void *data) +-{ +- struct bam_transaction *bam_txn = data; +- +- complete(&bam_txn->txn_done); +-} +- + static struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) + { + return container_of(chip, struct qcom_nand_host, chip); +@@ -629,8 +128,8 @@ static struct qcom_nand_host *to_qcom_na + static struct qcom_nand_controller * + get_qcom_nand_controller(struct nand_chip *chip) + { +- return container_of(chip->controller, struct qcom_nand_controller, +- controller); ++ return (struct qcom_nand_controller *) ++ ((u8 *)chip->controller - sizeof(struct qcom_nand_controller)); + } + + static u32 nandc_read(struct qcom_nand_controller *nandc, int offset) +@@ -644,23 +143,6 @@ static void nandc_write(struct qcom_nand + iowrite32(val, nandc->base + offset); + } + +-static void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu) +-{ +- if (!nandc->props->supports_bam) +- return; +- +- if (is_cpu) +- dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, +- MAX_REG_RD * +- sizeof(*nandc->reg_read_buf), +- DMA_FROM_DEVICE); +- else +- dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, +- MAX_REG_RD * +- sizeof(*nandc->reg_read_buf), +- DMA_FROM_DEVICE); +-} +- + /* Helper to check whether this is the last CW or not */ + static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw) + { +@@ -820,356 +302,6 @@ static void update_rw_regs(struct qcom_n + } + + /* +- * Maps the scatter gather list for DMA transfer and forms the DMA descriptor +- * for BAM. This descriptor will be added in the NAND DMA descriptor queue +- * which will be submitted to DMA engine. +- */ +-static int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc, +- struct dma_chan *chan, +- unsigned long flags) +-{ +- struct desc_info *desc; +- struct scatterlist *sgl; +- unsigned int sgl_cnt; +- int ret; +- struct bam_transaction *bam_txn = nandc->bam_txn; +- enum dma_transfer_direction dir_eng; +- struct dma_async_tx_descriptor *dma_desc; +- +- desc = kzalloc(sizeof(*desc), GFP_KERNEL); +- if (!desc) +- return -ENOMEM; +- +- if (chan == nandc->cmd_chan) { +- sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; +- sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; +- bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; +- dir_eng = DMA_MEM_TO_DEV; +- desc->dir = DMA_TO_DEVICE; +- } else if (chan == nandc->tx_chan) { +- sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; +- sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; +- bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; +- dir_eng = DMA_MEM_TO_DEV; +- desc->dir = DMA_TO_DEVICE; +- } else { +- sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; +- sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; +- bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; +- dir_eng = DMA_DEV_TO_MEM; +- desc->dir = DMA_FROM_DEVICE; +- } +- +- sg_mark_end(sgl + sgl_cnt - 1); +- ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); +- if (ret == 0) { +- dev_err(nandc->dev, "failure in mapping desc\n"); +- kfree(desc); +- return -ENOMEM; +- } +- +- desc->sgl_cnt = sgl_cnt; +- desc->bam_sgl = sgl; +- +- dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, +- flags); +- +- if (!dma_desc) { +- dev_err(nandc->dev, "failure in prep desc\n"); +- dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); +- kfree(desc); +- return -EINVAL; +- } +- +- desc->dma_desc = dma_desc; +- +- /* update last data/command descriptor */ +- if (chan == nandc->cmd_chan) +- bam_txn->last_cmd_desc = dma_desc; +- else +- bam_txn->last_data_desc = dma_desc; +- +- list_add_tail(&desc->node, &nandc->desc_list); +- +- return 0; +-} +- +-/* +- * Prepares the command descriptor for BAM DMA which will be used for NAND +- * register reads and writes. The command descriptor requires the command +- * to be formed in command element type so this function uses the command +- * element from bam transaction ce array and fills the same with required +- * data. A single SGL can contain multiple command elements so +- * NAND_BAM_NEXT_SGL will be used for starting the separate SGL +- * after the current command element. +- */ +-static int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, +- int reg_off, const void *vaddr, +- int size, unsigned int flags) +-{ +- int bam_ce_size; +- int i, ret; +- struct bam_cmd_element *bam_ce_buffer; +- struct bam_transaction *bam_txn = nandc->bam_txn; +- +- bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; +- +- /* fill the command desc */ +- for (i = 0; i < size; i++) { +- if (read) +- bam_prep_ce(&bam_ce_buffer[i], +- nandc_reg_phys(nandc, reg_off + 4 * i), +- BAM_READ_COMMAND, +- reg_buf_dma_addr(nandc, +- (__le32 *)vaddr + i)); +- else +- bam_prep_ce_le32(&bam_ce_buffer[i], +- nandc_reg_phys(nandc, reg_off + 4 * i), +- BAM_WRITE_COMMAND, +- *((__le32 *)vaddr + i)); +- } +- +- bam_txn->bam_ce_pos += size; +- +- /* use the separate sgl after this command */ +- if (flags & NAND_BAM_NEXT_SGL) { +- bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; +- bam_ce_size = (bam_txn->bam_ce_pos - +- bam_txn->bam_ce_start) * +- sizeof(struct bam_cmd_element); +- sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], +- bam_ce_buffer, bam_ce_size); +- bam_txn->cmd_sgl_pos++; +- bam_txn->bam_ce_start = bam_txn->bam_ce_pos; +- +- if (flags & NAND_BAM_NWD) { +- ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, +- DMA_PREP_FENCE | +- DMA_PREP_CMD); +- if (ret) +- return ret; +- } +- } +- +- return 0; +-} +- +-/* +- * Prepares the data descriptor for BAM DMA which will be used for NAND +- * data reads and writes. +- */ +-static int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, +- const void *vaddr, int size, unsigned int flags) +-{ +- int ret; +- struct bam_transaction *bam_txn = nandc->bam_txn; +- +- if (read) { +- sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], +- vaddr, size); +- bam_txn->rx_sgl_pos++; +- } else { +- sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], +- vaddr, size); +- bam_txn->tx_sgl_pos++; +- +- /* +- * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag +- * is not set, form the DMA descriptor +- */ +- if (!(flags & NAND_BAM_NO_EOT)) { +- ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, +- DMA_PREP_INTERRUPT); +- if (ret) +- return ret; +- } +- } +- +- return 0; +-} +- +-static int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, +- int reg_off, const void *vaddr, int size, +- bool flow_control) +-{ +- struct desc_info *desc; +- struct dma_async_tx_descriptor *dma_desc; +- struct scatterlist *sgl; +- struct dma_slave_config slave_conf; +- struct qcom_adm_peripheral_config periph_conf = {}; +- enum dma_transfer_direction dir_eng; +- int ret; +- +- desc = kzalloc(sizeof(*desc), GFP_KERNEL); +- if (!desc) +- return -ENOMEM; +- +- sgl = &desc->adm_sgl; +- +- sg_init_one(sgl, vaddr, size); +- +- if (read) { +- dir_eng = DMA_DEV_TO_MEM; +- desc->dir = DMA_FROM_DEVICE; +- } else { +- dir_eng = DMA_MEM_TO_DEV; +- desc->dir = DMA_TO_DEVICE; +- } +- +- ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir); +- if (ret == 0) { +- ret = -ENOMEM; +- goto err; +- } +- +- memset(&slave_conf, 0x00, sizeof(slave_conf)); +- +- slave_conf.device_fc = flow_control; +- if (read) { +- slave_conf.src_maxburst = 16; +- slave_conf.src_addr = nandc->base_dma + reg_off; +- if (nandc->data_crci) { +- periph_conf.crci = nandc->data_crci; +- slave_conf.peripheral_config = &periph_conf; +- slave_conf.peripheral_size = sizeof(periph_conf); +- } +- } else { +- slave_conf.dst_maxburst = 16; +- slave_conf.dst_addr = nandc->base_dma + reg_off; +- if (nandc->cmd_crci) { +- periph_conf.crci = nandc->cmd_crci; +- slave_conf.peripheral_config = &periph_conf; +- slave_conf.peripheral_size = sizeof(periph_conf); +- } +- } +- +- ret = dmaengine_slave_config(nandc->chan, &slave_conf); +- if (ret) { +- dev_err(nandc->dev, "failed to configure dma channel\n"); +- goto err; +- } +- +- dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0); +- if (!dma_desc) { +- dev_err(nandc->dev, "failed to prepare desc\n"); +- ret = -EINVAL; +- goto err; +- } +- +- desc->dma_desc = dma_desc; +- +- list_add_tail(&desc->node, &nandc->desc_list); +- +- return 0; +-err: +- kfree(desc); +- +- return ret; +-} +- +-/* +- * qcom_read_reg_dma: prepares a descriptor to read a given number of +- * contiguous registers to the reg_read_buf pointer +- * +- * @first: offset of the first register in the contiguous block +- * @num_regs: number of registers to read +- * @flags: flags to control DMA descriptor preparation +- */ +-static int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first, +- int num_regs, unsigned int flags) +-{ +- bool flow_control = false; +- void *vaddr; +- +- vaddr = nandc->reg_read_buf + nandc->reg_read_pos; +- nandc->reg_read_pos += num_regs; +- +- if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) +- first = dev_cmd_reg_addr(nandc, first); +- +- if (nandc->props->supports_bam) +- return qcom_prep_bam_dma_desc_cmd(nandc, true, first, vaddr, +- num_regs, flags); +- +- if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) +- flow_control = true; +- +- return qcom_prep_adm_dma_desc(nandc, true, first, vaddr, +- num_regs * sizeof(u32), flow_control); +-} +- +-/* +- * qcom_write_reg_dma: prepares a descriptor to write a given number of +- * contiguous registers +- * +- * @vaddr: contiguous memory from where register value will +- * be written +- * @first: offset of the first register in the contiguous block +- * @num_regs: number of registers to write +- * @flags: flags to control DMA descriptor preparation +- */ +-static int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, +- int first, int num_regs, unsigned int flags) +-{ +- bool flow_control = false; +- +- if (first == NAND_EXEC_CMD) +- flags |= NAND_BAM_NWD; +- +- if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) +- first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); +- +- if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) +- first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); +- +- if (nandc->props->supports_bam) +- return qcom_prep_bam_dma_desc_cmd(nandc, false, first, vaddr, +- num_regs, flags); +- +- if (first == NAND_FLASH_CMD) +- flow_control = true; +- +- return qcom_prep_adm_dma_desc(nandc, false, first, vaddr, +- num_regs * sizeof(u32), flow_control); +-} +- +-/* +- * qcom_read_data_dma: prepares a DMA descriptor to transfer data from the +- * controller's internal buffer to the buffer 'vaddr' +- * +- * @reg_off: offset within the controller's data buffer +- * @vaddr: virtual address of the buffer we want to write to +- * @size: DMA transaction size in bytes +- * @flags: flags to control DMA descriptor preparation +- */ +-static int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off, +- const u8 *vaddr, int size, unsigned int flags) +-{ +- if (nandc->props->supports_bam) +- return qcom_prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); +- +- return qcom_prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); +-} +- +-/* +- * qcom_write_data_dma: prepares a DMA descriptor to transfer data from +- * 'vaddr' to the controller's internal buffer +- * +- * @reg_off: offset within the controller's data buffer +- * @vaddr: virtual address of the buffer we want to read from +- * @size: DMA transaction size in bytes +- * @flags: flags to control DMA descriptor preparation +- */ +-static int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off, +- const u8 *vaddr, int size, unsigned int flags) +-{ +- if (nandc->props->supports_bam) +- return qcom_prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); +- +- return qcom_prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); +-} +- +-/* + * Helper to prepare DMA descriptors for configuring registers + * before reading a NAND page. + */ +@@ -1262,83 +394,6 @@ static void config_nand_cw_write(struct + NAND_BAM_NEXT_SGL); + } + +-/* helpers to submit/free our list of dma descriptors */ +-static int qcom_submit_descs(struct qcom_nand_controller *nandc) +-{ +- struct desc_info *desc, *n; +- dma_cookie_t cookie = 0; +- struct bam_transaction *bam_txn = nandc->bam_txn; +- int ret = 0; +- +- if (nandc->props->supports_bam) { +- if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { +- ret = qcom_prepare_bam_async_desc(nandc, nandc->rx_chan, 0); +- if (ret) +- goto err_unmap_free_desc; +- } +- +- if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { +- ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, +- DMA_PREP_INTERRUPT); +- if (ret) +- goto err_unmap_free_desc; +- } +- +- if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { +- ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, +- DMA_PREP_CMD); +- if (ret) +- goto err_unmap_free_desc; +- } +- } +- +- list_for_each_entry(desc, &nandc->desc_list, node) +- cookie = dmaengine_submit(desc->dma_desc); +- +- if (nandc->props->supports_bam) { +- bam_txn->last_cmd_desc->callback = qcom_qpic_bam_dma_done; +- bam_txn->last_cmd_desc->callback_param = bam_txn; +- +- dma_async_issue_pending(nandc->tx_chan); +- dma_async_issue_pending(nandc->rx_chan); +- dma_async_issue_pending(nandc->cmd_chan); +- +- if (!wait_for_completion_timeout(&bam_txn->txn_done, +- QPIC_NAND_COMPLETION_TIMEOUT)) +- ret = -ETIMEDOUT; +- } else { +- if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) +- ret = -ETIMEDOUT; +- } +- +-err_unmap_free_desc: +- /* +- * Unmap the dma sg_list and free the desc allocated by both +- * qcom_prepare_bam_async_desc() and qcom_prep_adm_dma_desc() functions. +- */ +- list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { +- list_del(&desc->node); +- +- if (nandc->props->supports_bam) +- dma_unmap_sg(nandc->dev, desc->bam_sgl, +- desc->sgl_cnt, desc->dir); +- else +- dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, +- desc->dir); +- +- kfree(desc); +- } +- +- return ret; +-} +- +-/* reset the register read buffer for next NAND operation */ +-static void qcom_clear_read_regs(struct qcom_nand_controller *nandc) +-{ +- nandc->reg_read_pos = 0; +- qcom_nandc_dev_to_mem(nandc, false); +-} +- + /* + * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read + * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS. +@@ -2975,141 +2030,14 @@ static const struct nand_controller_ops + .exec_op = qcom_nand_exec_op, + }; + +-static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) +-{ +- if (nandc->props->supports_bam) { +- if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) +- dma_unmap_single(nandc->dev, nandc->reg_read_dma, +- MAX_REG_RD * +- sizeof(*nandc->reg_read_buf), +- DMA_FROM_DEVICE); +- +- if (nandc->tx_chan) +- dma_release_channel(nandc->tx_chan); +- +- if (nandc->rx_chan) +- dma_release_channel(nandc->rx_chan); +- +- if (nandc->cmd_chan) +- dma_release_channel(nandc->cmd_chan); +- } else { +- if (nandc->chan) +- dma_release_channel(nandc->chan); +- } +-} +- +-static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) +-{ +- int ret; +- +- ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32)); +- if (ret) { +- dev_err(nandc->dev, "failed to set DMA mask\n"); +- return ret; +- } +- +- /* +- * we use the internal buffer for reading ONFI params, reading small +- * data like ID and status, and preforming read-copy-write operations +- * when writing to a codeword partially. 532 is the maximum possible +- * size of a codeword for our nand controller +- */ +- nandc->buf_size = 532; +- +- nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, GFP_KERNEL); +- if (!nandc->data_buffer) +- return -ENOMEM; +- +- nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), GFP_KERNEL); +- if (!nandc->regs) +- return -ENOMEM; +- +- nandc->reg_read_buf = devm_kcalloc(nandc->dev, MAX_REG_RD, +- sizeof(*nandc->reg_read_buf), +- GFP_KERNEL); +- if (!nandc->reg_read_buf) +- return -ENOMEM; +- +- if (nandc->props->supports_bam) { +- nandc->reg_read_dma = +- dma_map_single(nandc->dev, nandc->reg_read_buf, +- MAX_REG_RD * +- sizeof(*nandc->reg_read_buf), +- DMA_FROM_DEVICE); +- if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { +- dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); +- return -EIO; +- } +- +- nandc->tx_chan = dma_request_chan(nandc->dev, "tx"); +- if (IS_ERR(nandc->tx_chan)) { +- ret = PTR_ERR(nandc->tx_chan); +- nandc->tx_chan = NULL; +- dev_err_probe(nandc->dev, ret, +- "tx DMA channel request failed\n"); +- goto unalloc; +- } +- +- nandc->rx_chan = dma_request_chan(nandc->dev, "rx"); +- if (IS_ERR(nandc->rx_chan)) { +- ret = PTR_ERR(nandc->rx_chan); +- nandc->rx_chan = NULL; +- dev_err_probe(nandc->dev, ret, +- "rx DMA channel request failed\n"); +- goto unalloc; +- } +- +- nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd"); +- if (IS_ERR(nandc->cmd_chan)) { +- ret = PTR_ERR(nandc->cmd_chan); +- nandc->cmd_chan = NULL; +- dev_err_probe(nandc->dev, ret, +- "cmd DMA channel request failed\n"); +- goto unalloc; +- } +- +- /* +- * Initially allocate BAM transaction to read ONFI param page. +- * After detecting all the devices, this BAM transaction will +- * be freed and the next BAM transaction will be allocated with +- * maximum codeword size +- */ +- nandc->max_cwperpage = 1; +- nandc->bam_txn = qcom_alloc_bam_transaction(nandc); +- if (!nandc->bam_txn) { +- dev_err(nandc->dev, +- "failed to allocate bam transaction\n"); +- ret = -ENOMEM; +- goto unalloc; +- } +- } else { +- nandc->chan = dma_request_chan(nandc->dev, "rxtx"); +- if (IS_ERR(nandc->chan)) { +- ret = PTR_ERR(nandc->chan); +- nandc->chan = NULL; +- dev_err_probe(nandc->dev, ret, +- "rxtx DMA channel request failed\n"); +- return ret; +- } +- } +- +- INIT_LIST_HEAD(&nandc->desc_list); +- INIT_LIST_HEAD(&nandc->host_list); +- +- nand_controller_init(&nandc->controller); +- nandc->controller.ops = &qcom_nandc_ops; +- +- return 0; +-unalloc: +- qcom_nandc_unalloc(nandc); +- return ret; +-} +- + /* one time setup of a few nand controller registers */ + static int qcom_nandc_setup(struct qcom_nand_controller *nandc) + { + u32 nand_ctrl; + ++ nand_controller_init(nandc->controller); ++ nandc->controller->ops = &qcom_nandc_ops; ++ + /* kill onenand */ + if (!nandc->props->nandc_part_of_qpic) + nandc_write(nandc, SFLASHC_BURST_CFG, 0); +@@ -3248,7 +2176,7 @@ static int qcom_nand_host_init_and_regis + chip->legacy.block_bad = qcom_nandc_block_bad; + chip->legacy.block_markbad = qcom_nandc_block_markbad; + +- chip->controller = &nandc->controller; ++ chip->controller = nandc->controller; + chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA | + NAND_SKIP_BBTSCAN; + +@@ -3331,17 +2259,21 @@ static int qcom_nandc_parse_dt(struct pl + static int qcom_nandc_probe(struct platform_device *pdev) + { + struct qcom_nand_controller *nandc; ++ struct nand_controller *controller; + const void *dev_data; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + +- nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL); ++ nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc) + sizeof(*controller), ++ GFP_KERNEL); + if (!nandc) + return -ENOMEM; ++ controller = (struct nand_controller *)&nandc[1]; + + platform_set_drvdata(pdev, nandc); + nandc->dev = dev; ++ nandc->controller = controller; + + dev_data = of_device_get_match_data(dev); + if (!dev_data) { +--- /dev/null ++++ b/include/linux/mtd/nand-qpic-common.h +@@ -0,0 +1,468 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * QCOM QPIC common APIs header file ++ * ++ * Copyright (c) 2023 Qualcomm Inc. ++ * Authors: Md sadre Alam ++ * ++ */ ++#ifndef __MTD_NAND_QPIC_COMMON_H__ ++#define __MTD_NAND_QPIC_COMMON_H__ ++ ++/* NANDc reg offsets */ ++#define NAND_FLASH_CMD 0x00 ++#define NAND_ADDR0 0x04 ++#define NAND_ADDR1 0x08 ++#define NAND_FLASH_CHIP_SELECT 0x0c ++#define NAND_EXEC_CMD 0x10 ++#define NAND_FLASH_STATUS 0x14 ++#define NAND_BUFFER_STATUS 0x18 ++#define NAND_DEV0_CFG0 0x20 ++#define NAND_DEV0_CFG1 0x24 ++#define NAND_DEV0_ECC_CFG 0x28 ++#define NAND_AUTO_STATUS_EN 0x2c ++#define NAND_DEV1_CFG0 0x30 ++#define NAND_DEV1_CFG1 0x34 ++#define NAND_READ_ID 0x40 ++#define NAND_READ_STATUS 0x44 ++#define NAND_DEV_CMD0 0xa0 ++#define NAND_DEV_CMD1 0xa4 ++#define NAND_DEV_CMD2 0xa8 ++#define NAND_DEV_CMD_VLD 0xac ++#define SFLASHC_BURST_CFG 0xe0 ++#define NAND_ERASED_CW_DETECT_CFG 0xe8 ++#define NAND_ERASED_CW_DETECT_STATUS 0xec ++#define NAND_EBI2_ECC_BUF_CFG 0xf0 ++#define FLASH_BUF_ACC 0x100 ++ ++#define NAND_CTRL 0xf00 ++#define NAND_VERSION 0xf08 ++#define NAND_READ_LOCATION_0 0xf20 ++#define NAND_READ_LOCATION_1 0xf24 ++#define NAND_READ_LOCATION_2 0xf28 ++#define NAND_READ_LOCATION_3 0xf2c ++#define NAND_READ_LOCATION_LAST_CW_0 0xf40 ++#define NAND_READ_LOCATION_LAST_CW_1 0xf44 ++#define NAND_READ_LOCATION_LAST_CW_2 0xf48 ++#define NAND_READ_LOCATION_LAST_CW_3 0xf4c ++ ++/* dummy register offsets, used by qcom_write_reg_dma */ ++#define NAND_DEV_CMD1_RESTORE 0xdead ++#define NAND_DEV_CMD_VLD_RESTORE 0xbeef ++ ++/* NAND_FLASH_CMD bits */ ++#define PAGE_ACC BIT(4) ++#define LAST_PAGE BIT(5) ++ ++/* NAND_FLASH_CHIP_SELECT bits */ ++#define NAND_DEV_SEL 0 ++#define DM_EN BIT(2) ++ ++/* NAND_FLASH_STATUS bits */ ++#define FS_OP_ERR BIT(4) ++#define FS_READY_BSY_N BIT(5) ++#define FS_MPU_ERR BIT(8) ++#define FS_DEVICE_STS_ERR BIT(16) ++#define FS_DEVICE_WP BIT(23) ++ ++/* NAND_BUFFER_STATUS bits */ ++#define BS_UNCORRECTABLE_BIT BIT(8) ++#define BS_CORRECTABLE_ERR_MSK 0x1f ++ ++/* NAND_DEVn_CFG0 bits */ ++#define DISABLE_STATUS_AFTER_WRITE 4 ++#define CW_PER_PAGE 6 ++#define UD_SIZE_BYTES 9 ++#define UD_SIZE_BYTES_MASK GENMASK(18, 9) ++#define ECC_PARITY_SIZE_BYTES_RS 19 ++#define SPARE_SIZE_BYTES 23 ++#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23) ++#define NUM_ADDR_CYCLES 27 ++#define STATUS_BFR_READ 30 ++#define SET_RD_MODE_AFTER_STATUS 31 ++ ++/* NAND_DEVn_CFG0 bits */ ++#define DEV0_CFG1_ECC_DISABLE 0 ++#define WIDE_FLASH 1 ++#define NAND_RECOVERY_CYCLES 2 ++#define CS_ACTIVE_BSY 5 ++#define BAD_BLOCK_BYTE_NUM 6 ++#define BAD_BLOCK_IN_SPARE_AREA 16 ++#define WR_RD_BSY_GAP 17 ++#define ENABLE_BCH_ECC 27 ++ ++/* NAND_DEV0_ECC_CFG bits */ ++#define ECC_CFG_ECC_DISABLE 0 ++#define ECC_SW_RESET 1 ++#define ECC_MODE 4 ++#define ECC_PARITY_SIZE_BYTES_BCH 8 ++#define ECC_NUM_DATA_BYTES 16 ++#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16) ++#define ECC_FORCE_CLK_OPEN 30 ++ ++/* NAND_DEV_CMD1 bits */ ++#define READ_ADDR 0 ++ ++/* NAND_DEV_CMD_VLD bits */ ++#define READ_START_VLD BIT(0) ++#define READ_STOP_VLD BIT(1) ++#define WRITE_START_VLD BIT(2) ++#define ERASE_START_VLD BIT(3) ++#define SEQ_READ_START_VLD BIT(4) ++ ++/* NAND_EBI2_ECC_BUF_CFG bits */ ++#define NUM_STEPS 0 ++ ++/* NAND_ERASED_CW_DETECT_CFG bits */ ++#define ERASED_CW_ECC_MASK 1 ++#define AUTO_DETECT_RES 0 ++#define MASK_ECC BIT(ERASED_CW_ECC_MASK) ++#define RESET_ERASED_DET BIT(AUTO_DETECT_RES) ++#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) ++#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) ++#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) ++ ++/* NAND_ERASED_CW_DETECT_STATUS bits */ ++#define PAGE_ALL_ERASED BIT(7) ++#define CODEWORD_ALL_ERASED BIT(6) ++#define PAGE_ERASED BIT(5) ++#define CODEWORD_ERASED BIT(4) ++#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) ++#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) ++ ++/* NAND_READ_LOCATION_n bits */ ++#define READ_LOCATION_OFFSET 0 ++#define READ_LOCATION_SIZE 16 ++#define READ_LOCATION_LAST 31 ++ ++/* Version Mask */ ++#define NAND_VERSION_MAJOR_MASK 0xf0000000 ++#define NAND_VERSION_MAJOR_SHIFT 28 ++#define NAND_VERSION_MINOR_MASK 0x0fff0000 ++#define NAND_VERSION_MINOR_SHIFT 16 ++ ++/* NAND OP_CMDs */ ++#define OP_PAGE_READ 0x2 ++#define OP_PAGE_READ_WITH_ECC 0x3 ++#define OP_PAGE_READ_WITH_ECC_SPARE 0x4 ++#define OP_PAGE_READ_ONFI_READ 0x5 ++#define OP_PROGRAM_PAGE 0x6 ++#define OP_PAGE_PROGRAM_WITH_ECC 0x7 ++#define OP_PROGRAM_PAGE_SPARE 0x9 ++#define OP_BLOCK_ERASE 0xa ++#define OP_CHECK_STATUS 0xc ++#define OP_FETCH_ID 0xb ++#define OP_RESET_DEVICE 0xd ++ ++/* Default Value for NAND_DEV_CMD_VLD */ ++#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ ++ ERASE_START_VLD | SEQ_READ_START_VLD) ++ ++/* NAND_CTRL bits */ ++#define BAM_MODE_EN BIT(0) ++ ++/* ++ * the NAND controller performs reads/writes with ECC in 516 byte chunks. ++ * the driver calls the chunks 'step' or 'codeword' interchangeably ++ */ ++#define NANDC_STEP_SIZE 512 ++ ++/* ++ * the largest page size we support is 8K, this will have 16 steps/codewords ++ * of 512 bytes each ++ */ ++#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) ++ ++/* we read at most 3 registers per codeword scan */ ++#define MAX_REG_RD (3 * MAX_NUM_STEPS) ++ ++/* ECC modes supported by the controller */ ++#define ECC_NONE BIT(0) ++#define ECC_RS_4BIT BIT(1) ++#define ECC_BCH_4BIT BIT(2) ++#define ECC_BCH_8BIT BIT(3) ++ ++/* ++ * Returns the actual register address for all NAND_DEV_ registers ++ * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) ++ */ ++#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) ++ ++/* Returns the NAND register physical address */ ++#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset)) ++ ++/* Returns the dma address for reg read buffer */ ++#define reg_buf_dma_addr(chip, vaddr) \ ++ ((chip)->reg_read_dma + \ ++ ((u8 *)(vaddr) - (u8 *)(chip)->reg_read_buf)) ++ ++#define QPIC_PER_CW_CMD_ELEMENTS 32 ++#define QPIC_PER_CW_CMD_SGL 32 ++#define QPIC_PER_CW_DATA_SGL 8 ++ ++#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000) ++ ++/* ++ * Flags used in DMA descriptor preparation helper functions ++ * (i.e. qcom_read_reg_dma/qcom_write_reg_dma/qcom_read_data_dma/qcom_write_data_dma) ++ */ ++/* Don't set the EOT in current tx BAM sgl */ ++#define NAND_BAM_NO_EOT BIT(0) ++/* Set the NWD flag in current BAM sgl */ ++#define NAND_BAM_NWD BIT(1) ++/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ ++#define NAND_BAM_NEXT_SGL BIT(2) ++/* ++ * Erased codeword status is being used two times in single transfer so this ++ * flag will determine the current value of erased codeword status register ++ */ ++#define NAND_ERASED_CW_SET BIT(4) ++ ++#define MAX_ADDRESS_CYCLE 5 ++ ++/* ++ * This data type corresponds to the BAM transaction which will be used for all ++ * NAND transfers. ++ * @bam_ce - the array of BAM command elements ++ * @cmd_sgl - sgl for NAND BAM command pipe ++ * @data_sgl - sgl for NAND BAM consumer/producer pipe ++ * @last_data_desc - last DMA desc in data channel (tx/rx). ++ * @last_cmd_desc - last DMA desc in command channel. ++ * @txn_done - completion for NAND transfer. ++ * @bam_ce_pos - the index in bam_ce which is available for next sgl ++ * @bam_ce_start - the index in bam_ce which marks the start position ce ++ * for current sgl. It will be used for size calculation ++ * for current sgl ++ * @cmd_sgl_pos - current index in command sgl. ++ * @cmd_sgl_start - start index in command sgl. ++ * @tx_sgl_pos - current index in data sgl for tx. ++ * @tx_sgl_start - start index in data sgl for tx. ++ * @rx_sgl_pos - current index in data sgl for rx. ++ * @rx_sgl_start - start index in data sgl for rx. ++ */ ++struct bam_transaction { ++ struct bam_cmd_element *bam_ce; ++ struct scatterlist *cmd_sgl; ++ struct scatterlist *data_sgl; ++ struct dma_async_tx_descriptor *last_data_desc; ++ struct dma_async_tx_descriptor *last_cmd_desc; ++ struct completion txn_done; ++ u32 bam_ce_pos; ++ u32 bam_ce_start; ++ u32 cmd_sgl_pos; ++ u32 cmd_sgl_start; ++ u32 tx_sgl_pos; ++ u32 tx_sgl_start; ++ u32 rx_sgl_pos; ++ u32 rx_sgl_start; ++}; ++ ++/* ++ * This data type corresponds to the nand dma descriptor ++ * @dma_desc - low level DMA engine descriptor ++ * @list - list for desc_info ++ * ++ * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by ++ * ADM ++ * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM ++ * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM ++ * @dir - DMA transfer direction ++ */ ++struct desc_info { ++ struct dma_async_tx_descriptor *dma_desc; ++ struct list_head node; ++ ++ union { ++ struct scatterlist adm_sgl; ++ struct { ++ struct scatterlist *bam_sgl; ++ int sgl_cnt; ++ }; ++ }; ++ enum dma_data_direction dir; ++}; ++ ++/* ++ * holds the current register values that we want to write. acts as a contiguous ++ * chunk of memory which we use to write the controller registers through DMA. ++ */ ++struct nandc_regs { ++ __le32 cmd; ++ __le32 addr0; ++ __le32 addr1; ++ __le32 chip_sel; ++ __le32 exec; ++ ++ __le32 cfg0; ++ __le32 cfg1; ++ __le32 ecc_bch_cfg; ++ ++ __le32 clrflashstatus; ++ __le32 clrreadstatus; ++ ++ __le32 cmd1; ++ __le32 vld; ++ ++ __le32 orig_cmd1; ++ __le32 orig_vld; ++ ++ __le32 ecc_buf_cfg; ++ __le32 read_location0; ++ __le32 read_location1; ++ __le32 read_location2; ++ __le32 read_location3; ++ __le32 read_location_last0; ++ __le32 read_location_last1; ++ __le32 read_location_last2; ++ __le32 read_location_last3; ++ ++ __le32 erased_cw_detect_cfg_clr; ++ __le32 erased_cw_detect_cfg_set; ++}; ++ ++/* ++ * NAND controller data struct ++ * ++ * @dev: parent device ++ * ++ * @base: MMIO base ++ * ++ * @core_clk: controller clock ++ * @aon_clk: another controller clock ++ * ++ * @regs: a contiguous chunk of memory for DMA register ++ * writes. contains the register values to be ++ * written to controller ++ * ++ * @props: properties of current NAND controller, ++ * initialized via DT match data ++ * ++ * @controller: base controller structure ++ * @host_list: list containing all the chips attached to the ++ * controller ++ * ++ * @chan: dma channel ++ * @cmd_crci: ADM DMA CRCI for command flow control ++ * @data_crci: ADM DMA CRCI for data flow control ++ * ++ * @desc_list: DMA descriptor list (list of desc_infos) ++ * ++ * @data_buffer: our local DMA buffer for page read/writes, ++ * used when we can't use the buffer provided ++ * by upper layers directly ++ * @reg_read_buf: local buffer for reading back registers via DMA ++ * ++ * @base_phys: physical base address of controller registers ++ * @base_dma: dma base address of controller registers ++ * @reg_read_dma: contains dma address for register read buffer ++ * ++ * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf ++ * functions ++ * @max_cwperpage: maximum QPIC codewords required. calculated ++ * from all connected NAND devices pagesize ++ * ++ * @reg_read_pos: marker for data read in reg_read_buf ++ * ++ * @cmd1/vld: some fixed controller register values ++ * ++ * @exec_opwrite: flag to select correct number of code word ++ * while reading status ++ */ ++struct qcom_nand_controller { ++ struct device *dev; ++ ++ void __iomem *base; ++ ++ struct clk *core_clk; ++ struct clk *aon_clk; ++ ++ struct nandc_regs *regs; ++ struct bam_transaction *bam_txn; ++ ++ const struct qcom_nandc_props *props; ++ ++ struct nand_controller *controller; ++ struct list_head host_list; ++ ++ union { ++ /* will be used only by QPIC for BAM DMA */ ++ struct { ++ struct dma_chan *tx_chan; ++ struct dma_chan *rx_chan; ++ struct dma_chan *cmd_chan; ++ }; ++ ++ /* will be used only by EBI2 for ADM DMA */ ++ struct { ++ struct dma_chan *chan; ++ unsigned int cmd_crci; ++ unsigned int data_crci; ++ }; ++ }; ++ ++ struct list_head desc_list; ++ ++ u8 *data_buffer; ++ __le32 *reg_read_buf; ++ ++ phys_addr_t base_phys; ++ dma_addr_t base_dma; ++ dma_addr_t reg_read_dma; ++ ++ int buf_size; ++ int buf_count; ++ int buf_start; ++ unsigned int max_cwperpage; ++ ++ int reg_read_pos; ++ ++ u32 cmd1, vld; ++ bool exec_opwrite; ++}; ++ ++/* ++ * This data type corresponds to the NAND controller properties which varies ++ * among different NAND controllers. ++ * @ecc_modes - ecc mode for NAND ++ * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset ++ * @supports_bam - whether NAND controller is using BAM ++ * @nandc_part_of_qpic - whether NAND controller is part of qpic IP ++ * @qpic_version2 - flag to indicate QPIC IP version 2 ++ * @use_codeword_fixup - whether NAND has different layout for boot partitions ++ */ ++struct qcom_nandc_props { ++ u32 ecc_modes; ++ u32 dev_cmd_reg_start; ++ bool supports_bam; ++ bool nandc_part_of_qpic; ++ bool qpic_version2; ++ bool use_codeword_fixup; ++}; ++ ++void qcom_free_bam_transaction(struct qcom_nand_controller *nandc); ++struct bam_transaction *qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc); ++void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc); ++void qcom_qpic_bam_dma_done(void *data); ++void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu); ++int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc, ++ struct dma_chan *chan, unsigned long flags); ++int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, int size, unsigned int flags); ++int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, ++ const void *vaddr, int size, unsigned int flags); ++int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, int reg_off, ++ const void *vaddr, int size, bool flow_control); ++int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first, int num_regs, ++ unsigned int flags); ++int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, int first, ++ int num_regs, unsigned int flags); ++int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off, const u8 *vaddr, ++ int size, unsigned int flags); ++int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off, const u8 *vaddr, ++ int size, unsigned int flags); ++int qcom_submit_descs(struct qcom_nand_controller *nandc); ++void qcom_clear_read_regs(struct qcom_nand_controller *nandc); ++void qcom_nandc_unalloc(struct qcom_nand_controller *nandc); ++int qcom_nandc_alloc(struct qcom_nand_controller *nandc); ++#endif ++ diff --git a/target/linux/generic/backport-6.12/410-04-v6.14-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch b/target/linux/generic/backport-6.12/410-04-v6.14-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch new file mode 100644 index 00000000000..c19f681e9d9 --- /dev/null +++ b/target/linux/generic/backport-6.12/410-04-v6.14-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch @@ -0,0 +1,198 @@ +From 0c08080fd71cd5dd59643104b39d3c89d793ab3c Mon Sep 17 00:00:00 2001 +From: Md Sadre Alam +Date: Wed, 20 Nov 2024 14:45:03 +0530 +Subject: [PATCH 4/4] mtd: rawnand: qcom: use FIELD_PREP and GENMASK + +Use the bitfield macro FIELD_PREP, and GENMASK to +do the shift and mask in one go. This makes the code +more readable. + +Reviewed-by: Konrad Dybcio +Signed-off-by: Md Sadre Alam +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/raw/qcom_nandc.c | 97 ++++++++++++++-------------- + include/linux/mtd/nand-qpic-common.h | 31 +++++---- + 2 files changed, 67 insertions(+), 61 deletions(-) + +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -281,7 +281,7 @@ static void update_rw_regs(struct qcom_n + (num_cw - 1) << CW_PER_PAGE); + + cfg1 = cpu_to_le32(host->cfg1_raw); +- ecc_bch_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE); ++ ecc_bch_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE); + } + + nandc->regs->cmd = cmd; +@@ -1494,42 +1494,41 @@ static int qcom_nand_attach_chip(struct + host->cw_size = host->cw_data + ecc->bytes; + bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; + +- host->cfg0 = (cwperpage - 1) << CW_PER_PAGE +- | host->cw_data << UD_SIZE_BYTES +- | 0 << DISABLE_STATUS_AFTER_WRITE +- | 5 << NUM_ADDR_CYCLES +- | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS +- | 0 << STATUS_BFR_READ +- | 1 << SET_RD_MODE_AFTER_STATUS +- | host->spare_bytes << SPARE_SIZE_BYTES; +- +- host->cfg1 = 7 << NAND_RECOVERY_CYCLES +- | 0 << CS_ACTIVE_BSY +- | bad_block_byte << BAD_BLOCK_BYTE_NUM +- | 0 << BAD_BLOCK_IN_SPARE_AREA +- | 2 << WR_RD_BSY_GAP +- | wide_bus << WIDE_FLASH +- | host->bch_enabled << ENABLE_BCH_ECC; +- +- host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE +- | host->cw_size << UD_SIZE_BYTES +- | 5 << NUM_ADDR_CYCLES +- | 0 << SPARE_SIZE_BYTES; +- +- host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES +- | 0 << CS_ACTIVE_BSY +- | 17 << BAD_BLOCK_BYTE_NUM +- | 1 << BAD_BLOCK_IN_SPARE_AREA +- | 2 << WR_RD_BSY_GAP +- | wide_bus << WIDE_FLASH +- | 1 << DEV0_CFG1_ECC_DISABLE; +- +- host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE +- | 0 << ECC_SW_RESET +- | host->cw_data << ECC_NUM_DATA_BYTES +- | 1 << ECC_FORCE_CLK_OPEN +- | ecc_mode << ECC_MODE +- | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH; ++ host->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, host->cw_data) | ++ FIELD_PREP(DISABLE_STATUS_AFTER_WRITE, 0) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) | ++ FIELD_PREP(ECC_PARITY_SIZE_BYTES_RS, host->ecc_bytes_hw) | ++ FIELD_PREP(STATUS_BFR_READ, 0) | ++ FIELD_PREP(SET_RD_MODE_AFTER_STATUS, 1) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, host->spare_bytes); ++ ++ host->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, bad_block_byte) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 0) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) | ++ FIELD_PREP(WIDE_FLASH, wide_bus) | ++ FIELD_PREP(ENABLE_BCH_ECC, host->bch_enabled); ++ ++ host->cfg0_raw = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, host->cw_size) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0); ++ ++ host->cfg1_raw = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) | ++ FIELD_PREP(CS_ACTIVE_BSY, 0) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) | ++ FIELD_PREP(WIDE_FLASH, wide_bus) | ++ FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1); ++ ++ host->ecc_bch_cfg = FIELD_PREP(ECC_CFG_ECC_DISABLE, !host->bch_enabled) | ++ FIELD_PREP(ECC_SW_RESET, 0) | ++ FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, host->cw_data) | ++ FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | ++ FIELD_PREP(ECC_MODE_MASK, ecc_mode) | ++ FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, host->ecc_bytes_hw); + + if (!nandc->props->qpic_version2) + host->ecc_buf_cfg = 0x203 << NUM_STEPS; +@@ -1887,21 +1886,21 @@ static int qcom_param_page_type_exec(str + nandc->regs->addr0 = 0; + nandc->regs->addr1 = 0; + +- nandc->regs->cfg0 = cpu_to_le32(0 << CW_PER_PAGE | +- 512 << UD_SIZE_BYTES | +- 5 << NUM_ADDR_CYCLES | +- 0 << SPARE_SIZE_BYTES); +- +- nandc->regs->cfg1 = cpu_to_le32(7 << NAND_RECOVERY_CYCLES | +- 0 << CS_ACTIVE_BSY | +- 17 << BAD_BLOCK_BYTE_NUM | +- 1 << BAD_BLOCK_IN_SPARE_AREA | +- 2 << WR_RD_BSY_GAP | +- 0 << WIDE_FLASH | +- 1 << DEV0_CFG1_ECC_DISABLE); ++ host->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, 0) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, 512) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0); ++ ++ host->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) | ++ FIELD_PREP(CS_ACTIVE_BSY, 0) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) | ++ FIELD_PREP(WIDE_FLASH, 0) | ++ FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1); + + if (!nandc->props->qpic_version2) +- nandc->regs->ecc_buf_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE); ++ nandc->regs->ecc_buf_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE); + + /* configure CMD1 and VLD for ONFI param probing in QPIC v1 */ + if (!nandc->props->qpic_version2) { +--- a/include/linux/mtd/nand-qpic-common.h ++++ b/include/linux/mtd/nand-qpic-common.h +@@ -70,35 +70,42 @@ + #define BS_CORRECTABLE_ERR_MSK 0x1f + + /* NAND_DEVn_CFG0 bits */ +-#define DISABLE_STATUS_AFTER_WRITE 4 ++#define DISABLE_STATUS_AFTER_WRITE BIT(4) + #define CW_PER_PAGE 6 ++#define CW_PER_PAGE_MASK GENMASK(8, 6) + #define UD_SIZE_BYTES 9 + #define UD_SIZE_BYTES_MASK GENMASK(18, 9) +-#define ECC_PARITY_SIZE_BYTES_RS 19 ++#define ECC_PARITY_SIZE_BYTES_RS GENMASK(22, 19) + #define SPARE_SIZE_BYTES 23 + #define SPARE_SIZE_BYTES_MASK GENMASK(26, 23) + #define NUM_ADDR_CYCLES 27 +-#define STATUS_BFR_READ 30 +-#define SET_RD_MODE_AFTER_STATUS 31 ++#define NUM_ADDR_CYCLES_MASK GENMASK(29, 27) ++#define STATUS_BFR_READ BIT(30) ++#define SET_RD_MODE_AFTER_STATUS BIT(31) + + /* NAND_DEVn_CFG0 bits */ +-#define DEV0_CFG1_ECC_DISABLE 0 +-#define WIDE_FLASH 1 ++#define DEV0_CFG1_ECC_DISABLE BIT(0) ++#define WIDE_FLASH BIT(1) + #define NAND_RECOVERY_CYCLES 2 +-#define CS_ACTIVE_BSY 5 ++#define NAND_RECOVERY_CYCLES_MASK GENMASK(4, 2) ++#define CS_ACTIVE_BSY BIT(5) + #define BAD_BLOCK_BYTE_NUM 6 +-#define BAD_BLOCK_IN_SPARE_AREA 16 ++#define BAD_BLOCK_BYTE_NUM_MASK GENMASK(15, 6) ++#define BAD_BLOCK_IN_SPARE_AREA BIT(16) + #define WR_RD_BSY_GAP 17 +-#define ENABLE_BCH_ECC 27 ++#define WR_RD_BSY_GAP_MASK GENMASK(22, 17) ++#define ENABLE_BCH_ECC BIT(27) + + /* NAND_DEV0_ECC_CFG bits */ +-#define ECC_CFG_ECC_DISABLE 0 +-#define ECC_SW_RESET 1 ++#define ECC_CFG_ECC_DISABLE BIT(0) ++#define ECC_SW_RESET BIT(1) + #define ECC_MODE 4 ++#define ECC_MODE_MASK GENMASK(5, 4) + #define ECC_PARITY_SIZE_BYTES_BCH 8 ++#define ECC_PARITY_SIZE_BYTES_BCH_MASK GENMASK(12, 8) + #define ECC_NUM_DATA_BYTES 16 + #define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16) +-#define ECC_FORCE_CLK_OPEN 30 ++#define ECC_FORCE_CLK_OPEN BIT(30) + + /* NAND_DEV_CMD1 bits */ + #define READ_ADDR 0 diff --git a/target/linux/generic/backport-6.12/410-05-v6.14-mtd-rawnand-qcom-fix-broken-config-in-qcom_param_pag.patch b/target/linux/generic/backport-6.12/410-05-v6.14-mtd-rawnand-qcom-fix-broken-config-in-qcom_param_pag.patch new file mode 100644 index 00000000000..238a1f9d93f --- /dev/null +++ b/target/linux/generic/backport-6.12/410-05-v6.14-mtd-rawnand-qcom-fix-broken-config-in-qcom_param_pag.patch @@ -0,0 +1,64 @@ +From 9d4ffbcfde283f2a87ea45128ddf7e6651facdd9 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 7 Feb 2025 20:42:38 +0100 +Subject: [PATCH] mtd: rawnand: qcom: fix broken config in + qcom_param_page_type_exec + +Fix broken config in qcom_param_page_type_exec caused by copy-paste error +from commit 0c08080fd71c ("mtd: rawnand: qcom: use FIELD_PREP and GENMASK") + +In qcom_param_page_type_exec the value needs to be set to +nandc->regs->cfg0 instead of host->cfg0. This wrong configuration caused +the Qcom NANDC driver to malfunction on any device that makes use of it +(IPQ806x, IPQ40xx, IPQ807x, IPQ60xx) with the following error: + +[ 0.885369] nand: device found, Manufacturer ID: 0x2c, Chip ID: 0xaa +[ 0.885909] nand: Micron NAND 256MiB 1,8V 8-bit +[ 0.892499] nand: 256 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64 +[ 0.896823] nand: ECC (step, strength) = (512, 8) does not fit in OOB +[ 0.896836] qcom-nandc 79b0000.nand-controller: No valid ECC settings possible +[ 0.910996] bam-dma-engine 7984000.dma-controller: Cannot free busy channel +[ 0.918070] qcom-nandc: probe of 79b0000.nand-controller failed with error -28 + +Restore original configuration fix the problem and makes the driver work +again. + +Cc: stable@vger.kernel.org +Fixes: 0c08080fd71c ("mtd: rawnand: qcom: use FIELD_PREP and GENMASK") +Signed-off-by: Christian Marangi +--- + drivers/mtd/nand/raw/qcom_nandc.c | 24 ++++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -1886,18 +1886,18 @@ static int qcom_param_page_type_exec(str + nandc->regs->addr0 = 0; + nandc->regs->addr1 = 0; + +- host->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, 0) | +- FIELD_PREP(UD_SIZE_BYTES_MASK, 512) | +- FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) | +- FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0); ++ nandc->regs->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, 0) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, 512) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0); + +- host->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) | +- FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) | +- FIELD_PREP(CS_ACTIVE_BSY, 0) | +- FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) | +- FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) | +- FIELD_PREP(WIDE_FLASH, 0) | +- FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1); ++ nandc->regs->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) | ++ FIELD_PREP(CS_ACTIVE_BSY, 0) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) | ++ FIELD_PREP(WIDE_FLASH, 0) | ++ FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1); + + if (!nandc->props->qpic_version2) + nandc->regs->ecc_buf_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE); diff --git a/target/linux/generic/backport-6.12/410-06-v6.14-mtd-rawnand-qcom-Fix-build-issue-on-x86-architecture.patch b/target/linux/generic/backport-6.12/410-06-v6.14-mtd-rawnand-qcom-Fix-build-issue-on-x86-architecture.patch new file mode 100644 index 00000000000..67beed38d52 --- /dev/null +++ b/target/linux/generic/backport-6.12/410-06-v6.14-mtd-rawnand-qcom-Fix-build-issue-on-x86-architecture.patch @@ -0,0 +1,77 @@ +From b9371866799d67a80be0ea9e01bd41987db22f26 Mon Sep 17 00:00:00 2001 +From: Md Sadre Alam +Date: Mon, 6 Jan 2025 18:45:58 +0530 +Subject: [PATCH] mtd: rawnand: qcom: Fix build issue on x86 architecture +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fix a buffer overflow issue in qcom_clear_bam_transaction by using +struct_group to group related fields and avoid FORTIFY_SOURCE warnings. + +On x86 architecture, the following error occurs due to warnings being +treated as errors: + +In function ‘fortify_memset_chk’, + inlined from ‘qcom_clear_bam_transaction’ at +drivers/mtd/nand/qpic_common.c:88:2: +./include/linux/fortify-string.h:480:25: error: call to ‘__write_overflow_field’ +declared with attribute warning: detected write beyond size of field +(1st parameter); maybe use struct_group()? [-Werror=attribute-warning] + 480 | __write_overflow_field(p_size_field, size); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + LD [M] drivers/mtd/nand/nandcore.o + CC [M] drivers/w1/masters/mxc_w1.o +cc1: all warnings being treated as errors + +This patch addresses the issue by grouping the related fields in +struct bam_transaction using struct_group and updating the memset call +accordingly. + +Fixes: 8c52932da5e6 ("mtd: rawnand: qcom: cleanup qcom_nandc driver") +Signed-off-by: Md Sadre Alam +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/qpic_common.c | 2 +- + include/linux/mtd/nand-qpic-common.h | 19 +++++++++++-------- + 2 files changed, 12 insertions(+), 9 deletions(-) + +--- a/drivers/mtd/nand/qpic_common.c ++++ b/drivers/mtd/nand/qpic_common.c +@@ -85,7 +85,7 @@ void qcom_clear_bam_transaction(struct q + if (!nandc->props->supports_bam) + return; + +- memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8); ++ memset(&bam_txn->bam_positions, 0, sizeof(bam_txn->bam_positions)); + bam_txn->last_data_desc = NULL; + + sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * +--- a/include/linux/mtd/nand-qpic-common.h ++++ b/include/linux/mtd/nand-qpic-common.h +@@ -254,14 +254,17 @@ struct bam_transaction { + struct dma_async_tx_descriptor *last_data_desc; + struct dma_async_tx_descriptor *last_cmd_desc; + struct completion txn_done; +- u32 bam_ce_pos; +- u32 bam_ce_start; +- u32 cmd_sgl_pos; +- u32 cmd_sgl_start; +- u32 tx_sgl_pos; +- u32 tx_sgl_start; +- u32 rx_sgl_pos; +- u32 rx_sgl_start; ++ struct_group(bam_positions, ++ u32 bam_ce_pos; ++ u32 bam_ce_start; ++ u32 cmd_sgl_pos; ++ u32 cmd_sgl_start; ++ u32 tx_sgl_pos; ++ u32 tx_sgl_start; ++ u32 rx_sgl_pos; ++ u32 rx_sgl_start; ++ ++ ); + }; + + /* diff --git a/target/linux/generic/backport-6.12/410-07-v6.15-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Inte.patch b/target/linux/generic/backport-6.12/410-07-v6.15-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Inte.patch new file mode 100644 index 00000000000..310d90233f9 --- /dev/null +++ b/target/linux/generic/backport-6.12/410-07-v6.15-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Inte.patch @@ -0,0 +1,1737 @@ +From 7304d1909080ef0c9da703500a97f46c98393fcd Mon Sep 17 00:00:00 2001 +From: Md Sadre Alam +Date: Mon, 24 Feb 2025 16:44:14 +0530 +Subject: [PATCH] spi: spi-qpic: add driver for QCOM SPI NAND flash Interface + +This driver implements support for the SPI-NAND mode of QCOM NAND Flash +Interface as a SPI-MEM controller with pipelined ECC capability. + +Co-developed-by: Sricharan Ramabadhran +Signed-off-by: Sricharan Ramabadhran +Co-developed-by: Varadarajan Narayanan +Signed-off-by: Varadarajan Narayanan +Signed-off-by: Md Sadre Alam +Link: https://patch.msgid.link/20250224111414.2809669-3-quic_mdalam@quicinc.com +Signed-off-by: Mark Brown +--- + drivers/mtd/nand/Makefile | 4 + + drivers/spi/Kconfig | 9 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-qpic-snand.c | 1631 ++++++++++++++++++++++++++ + include/linux/mtd/nand-qpic-common.h | 7 + + 5 files changed, 1652 insertions(+) + create mode 100644 drivers/spi/spi-qpic-snand.c + +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -3,7 +3,11 @@ + nandcore-objs := core.o bbt.o + obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o + obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o ++ifeq ($(CONFIG_SPI_QPIC_SNAND),y) ++obj-$(CONFIG_SPI_QPIC_SNAND) += qpic_common.o ++else + obj-$(CONFIG_MTD_NAND_QCOM) += qpic_common.o ++endif + obj-y += onenand/ + obj-y += raw/ + obj-y += spi/ +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -898,6 +898,15 @@ config SPI_QCOM_QSPI + help + QSPI(Quad SPI) driver for Qualcomm QSPI controller. + ++config SPI_QPIC_SNAND ++ bool "QPIC SNAND controller" ++ depends on ARCH_QCOM || COMPILE_TEST ++ select MTD ++ help ++ QPIC_SNAND (QPIC SPI NAND) driver for Qualcomm QPIC controller. ++ QPIC controller supports both parallel nand and serial nand. ++ This config will enable serial nand driver for QPIC controller. ++ + config SPI_QUP + tristate "Qualcomm SPI controller with QUP interface" + depends on ARCH_QCOM || COMPILE_TEST +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -114,6 +114,7 @@ obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx- + obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o + obj-$(CONFIG_SPI_QCOM_GENI) += spi-geni-qcom.o + obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom-qspi.o ++obj-$(CONFIG_SPI_QPIC_SNAND) += spi-qpic-snand.o + obj-$(CONFIG_SPI_QUP) += spi-qup.o + obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o + obj-$(CONFIG_SPI_ROCKCHIP_SFC) += spi-rockchip-sfc.o +--- /dev/null ++++ b/drivers/spi/spi-qpic-snand.c +@@ -0,0 +1,1631 @@ ++/* ++ * SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. ++ * ++ * Authors: ++ * Md Sadre Alam ++ * Sricharan R ++ * Varadarajan Narayanan ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define NAND_FLASH_SPI_CFG 0xc0 ++#define NAND_NUM_ADDR_CYCLES 0xc4 ++#define NAND_BUSY_CHECK_WAIT_CNT 0xc8 ++#define NAND_FLASH_FEATURES 0xf64 ++ ++/* QSPI NAND config reg bits */ ++#define LOAD_CLK_CNTR_INIT_EN BIT(28) ++#define CLK_CNTR_INIT_VAL_VEC 0x924 ++#define CLK_CNTR_INIT_VAL_VEC_MASK GENMASK(27, 16) ++#define FEA_STATUS_DEV_ADDR 0xc0 ++#define FEA_STATUS_DEV_ADDR_MASK GENMASK(15, 8) ++#define SPI_CFG BIT(0) ++#define SPI_NUM_ADDR 0xDA4DB ++#define SPI_WAIT_CNT 0x10 ++#define QPIC_QSPI_NUM_CS 1 ++#define SPI_TRANSFER_MODE_x1 BIT(29) ++#define SPI_TRANSFER_MODE_x4 (3 << 29) ++#define SPI_WP BIT(28) ++#define SPI_HOLD BIT(27) ++#define QPIC_SET_FEATURE BIT(31) ++ ++#define SPINAND_RESET 0xff ++#define SPINAND_READID 0x9f ++#define SPINAND_GET_FEATURE 0x0f ++#define SPINAND_SET_FEATURE 0x1f ++#define SPINAND_READ 0x13 ++#define SPINAND_ERASE 0xd8 ++#define SPINAND_WRITE_EN 0x06 ++#define SPINAND_PROGRAM_EXECUTE 0x10 ++#define SPINAND_PROGRAM_LOAD 0x84 ++ ++#define ACC_FEATURE 0xe ++#define BAD_BLOCK_MARKER_SIZE 0x2 ++#define OOB_BUF_SIZE 128 ++#define ecceng_to_qspi(eng) container_of(eng, struct qpic_spi_nand, ecc_eng) ++ ++struct qpic_snand_op { ++ u32 cmd_reg; ++ u32 addr1_reg; ++ u32 addr2_reg; ++}; ++ ++struct snandc_read_status { ++ __le32 snandc_flash; ++ __le32 snandc_buffer; ++ __le32 snandc_erased_cw; ++}; ++ ++/* ++ * ECC state struct ++ * @corrected: ECC corrected ++ * @bitflips: Max bit flip ++ * @failed: ECC failed ++ */ ++struct qcom_ecc_stats { ++ u32 corrected; ++ u32 bitflips; ++ u32 failed; ++}; ++ ++struct qpic_ecc { ++ struct device *dev; ++ int ecc_bytes_hw; ++ int spare_bytes; ++ int bbm_size; ++ int ecc_mode; ++ int bytes; ++ int steps; ++ int step_size; ++ int strength; ++ int cw_size; ++ int cw_data; ++ u32 cfg0; ++ u32 cfg1; ++ u32 cfg0_raw; ++ u32 cfg1_raw; ++ u32 ecc_buf_cfg; ++ u32 ecc_bch_cfg; ++ u32 clrflashstatus; ++ u32 clrreadstatus; ++ bool bch_enabled; ++}; ++ ++struct qpic_spi_nand { ++ struct qcom_nand_controller *snandc; ++ struct spi_controller *ctlr; ++ struct mtd_info *mtd; ++ struct clk *iomacro_clk; ++ struct qpic_ecc *ecc; ++ struct qcom_ecc_stats ecc_stats; ++ struct nand_ecc_engine ecc_eng; ++ u8 *data_buf; ++ u8 *oob_buf; ++ u32 wlen; ++ __le32 addr1; ++ __le32 addr2; ++ __le32 cmd; ++ u32 num_cw; ++ bool oob_rw; ++ bool page_rw; ++ bool raw_rw; ++}; ++ ++static void qcom_spi_set_read_loc_first(struct qcom_nand_controller *snandc, ++ int reg, int cw_offset, int read_size, ++ int is_last_read_loc) ++{ ++ __le32 locreg_val; ++ u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | ++ ((read_size) << READ_LOCATION_SIZE) | ((is_last_read_loc) ++ << READ_LOCATION_LAST)); ++ ++ locreg_val = cpu_to_le32(val); ++ ++ if (reg == NAND_READ_LOCATION_0) ++ snandc->regs->read_location0 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_1) ++ snandc->regs->read_location1 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_2) ++ snandc->regs->read_location1 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_3) ++ snandc->regs->read_location3 = locreg_val; ++} ++ ++static void qcom_spi_set_read_loc_last(struct qcom_nand_controller *snandc, ++ int reg, int cw_offset, int read_size, ++ int is_last_read_loc) ++{ ++ __le32 locreg_val; ++ u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | ++ ((read_size) << READ_LOCATION_SIZE) | ((is_last_read_loc) ++ << READ_LOCATION_LAST)); ++ ++ locreg_val = cpu_to_le32(val); ++ ++ if (reg == NAND_READ_LOCATION_LAST_CW_0) ++ snandc->regs->read_location_last0 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_LAST_CW_1) ++ snandc->regs->read_location_last1 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_LAST_CW_2) ++ snandc->regs->read_location_last2 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_LAST_CW_3) ++ snandc->regs->read_location_last3 = locreg_val; ++} ++ ++static struct qcom_nand_controller *nand_to_qcom_snand(struct nand_device *nand) ++{ ++ struct nand_ecc_engine *eng = nand->ecc.engine; ++ struct qpic_spi_nand *qspi = ecceng_to_qspi(eng); ++ ++ return qspi->snandc; ++} ++ ++static int qcom_spi_init(struct qcom_nand_controller *snandc) ++{ ++ u32 snand_cfg_val = 0x0; ++ int ret; ++ ++ snand_cfg_val = FIELD_PREP(CLK_CNTR_INIT_VAL_VEC_MASK, CLK_CNTR_INIT_VAL_VEC) | ++ FIELD_PREP(LOAD_CLK_CNTR_INIT_EN, 0) | ++ FIELD_PREP(FEA_STATUS_DEV_ADDR_MASK, FEA_STATUS_DEV_ADDR) | ++ FIELD_PREP(SPI_CFG, 0); ++ ++ snandc->regs->spi_cfg = cpu_to_le32(snand_cfg_val); ++ snandc->regs->num_addr_cycle = cpu_to_le32(SPI_NUM_ADDR); ++ snandc->regs->busy_wait_cnt = cpu_to_le32(SPI_WAIT_CNT); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->spi_cfg, NAND_FLASH_SPI_CFG, 1, 0); ++ ++ snand_cfg_val &= ~LOAD_CLK_CNTR_INIT_EN; ++ snandc->regs->spi_cfg = cpu_to_le32(snand_cfg_val); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->spi_cfg, NAND_FLASH_SPI_CFG, 1, 0); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->num_addr_cycle, NAND_NUM_ADDR_CYCLES, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->busy_wait_cnt, NAND_BUSY_CHECK_WAIT_CNT, 1, ++ NAND_BAM_NEXT_SGL); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure in submitting spi init descriptor\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int qcom_spi_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_device *nand = mtd_to_nanddev(mtd); ++ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct qpic_ecc *qecc = snandc->qspi->ecc; ++ ++ if (section > 1) ++ return -ERANGE; ++ ++ oobregion->length = qecc->ecc_bytes_hw + qecc->spare_bytes; ++ oobregion->offset = mtd->oobsize - oobregion->length; ++ ++ return 0; ++} ++ ++static int qcom_spi_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_device *nand = mtd_to_nanddev(mtd); ++ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct qpic_ecc *qecc = snandc->qspi->ecc; ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->length = qecc->steps * 4; ++ oobregion->offset = ((qecc->steps - 1) * qecc->bytes) + qecc->bbm_size; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops qcom_spi_ooblayout = { ++ .ecc = qcom_spi_ooblayout_ecc, ++ .free = qcom_spi_ooblayout_free, ++}; ++ ++static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) ++{ ++ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct nand_ecc_props *conf = &nand->ecc.ctx.conf; ++ struct mtd_info *mtd = nanddev_to_mtd(nand); ++ int cwperpage, bad_block_byte; ++ struct qpic_ecc *ecc_cfg; ++ ++ cwperpage = mtd->writesize / NANDC_STEP_SIZE; ++ snandc->qspi->num_cw = cwperpage; ++ ++ ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL); ++ if (!ecc_cfg) ++ return -ENOMEM; ++ snandc->qspi->oob_buf = kzalloc(mtd->writesize + mtd->oobsize, ++ GFP_KERNEL); ++ if (!snandc->qspi->oob_buf) ++ return -ENOMEM; ++ ++ memset(snandc->qspi->oob_buf, 0xff, mtd->writesize + mtd->oobsize); ++ ++ nand->ecc.ctx.priv = ecc_cfg; ++ snandc->qspi->mtd = mtd; ++ ++ ecc_cfg->ecc_bytes_hw = 7; ++ ecc_cfg->spare_bytes = 4; ++ ecc_cfg->bbm_size = 1; ++ ecc_cfg->bch_enabled = true; ++ ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size; ++ ++ ecc_cfg->steps = 4; ++ ecc_cfg->strength = 4; ++ ecc_cfg->step_size = 512; ++ ecc_cfg->cw_data = 516; ++ ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes; ++ bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1; ++ ++ mtd_set_ooblayout(mtd, &qcom_spi_ooblayout); ++ ++ ecc_cfg->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, ecc_cfg->cw_data) | ++ FIELD_PREP(DISABLE_STATUS_AFTER_WRITE, 1) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 3) | ++ FIELD_PREP(ECC_PARITY_SIZE_BYTES_RS, ecc_cfg->ecc_bytes_hw) | ++ FIELD_PREP(STATUS_BFR_READ, 0) | ++ FIELD_PREP(SET_RD_MODE_AFTER_STATUS, 1) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, ecc_cfg->spare_bytes); ++ ++ ecc_cfg->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 0) | ++ FIELD_PREP(CS_ACTIVE_BSY, 0) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, bad_block_byte) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 0) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 20) | ++ FIELD_PREP(WIDE_FLASH, 0) | ++ FIELD_PREP(ENABLE_BCH_ECC, ecc_cfg->bch_enabled); ++ ++ ecc_cfg->cfg0_raw = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 3) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, ecc_cfg->cw_size) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0); ++ ++ ecc_cfg->cfg1_raw = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 0) | ++ FIELD_PREP(CS_ACTIVE_BSY, 0) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 20) | ++ FIELD_PREP(WIDE_FLASH, 0) | ++ FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1); ++ ++ ecc_cfg->ecc_bch_cfg = FIELD_PREP(ECC_CFG_ECC_DISABLE, !ecc_cfg->bch_enabled) | ++ FIELD_PREP(ECC_SW_RESET, 0) | ++ FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) | ++ FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | ++ FIELD_PREP(ECC_MODE_MASK, 0) | ++ FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); ++ ++ ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS; ++ ecc_cfg->clrflashstatus = FS_READY_BSY_N; ++ ecc_cfg->clrreadstatus = 0xc0; ++ ++ conf->step_size = ecc_cfg->step_size; ++ conf->strength = ecc_cfg->strength; ++ ++ snandc->regs->erased_cw_detect_cfg_clr = cpu_to_le32(CLR_ERASED_PAGE_DET); ++ snandc->regs->erased_cw_detect_cfg_set = cpu_to_le32(SET_ERASED_PAGE_DET); ++ ++ dev_dbg(snandc->dev, "ECC strength: %u bits per %u bytes\n", ++ ecc_cfg->strength, ecc_cfg->step_size); ++ ++ return 0; ++} ++ ++static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand) ++{ ++ struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand); ++ ++ kfree(ecc_cfg); ++} ++ ++static int qcom_spi_ecc_prepare_io_req_pipelined(struct nand_device *nand, ++ struct nand_page_io_req *req) ++{ ++ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand); ++ ++ snandc->qspi->ecc = ecc_cfg; ++ snandc->qspi->raw_rw = false; ++ snandc->qspi->oob_rw = false; ++ snandc->qspi->page_rw = false; ++ ++ if (req->datalen) ++ snandc->qspi->page_rw = true; ++ ++ if (req->ooblen) ++ snandc->qspi->oob_rw = true; ++ ++ if (req->mode == MTD_OPS_RAW) ++ snandc->qspi->raw_rw = true; ++ ++ return 0; ++} ++ ++static int qcom_spi_ecc_finish_io_req_pipelined(struct nand_device *nand, ++ struct nand_page_io_req *req) ++{ ++ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct mtd_info *mtd = nanddev_to_mtd(nand); ++ ++ if (req->mode == MTD_OPS_RAW || req->type != NAND_PAGE_READ) ++ return 0; ++ ++ if (snandc->qspi->ecc_stats.failed) ++ mtd->ecc_stats.failed += snandc->qspi->ecc_stats.failed; ++ else ++ mtd->ecc_stats.corrected += snandc->qspi->ecc_stats.corrected; ++ ++ if (snandc->qspi->ecc_stats.failed) ++ return -EBADMSG; ++ else ++ return snandc->qspi->ecc_stats.bitflips; ++} ++ ++static struct nand_ecc_engine_ops qcom_spi_ecc_engine_ops_pipelined = { ++ .init_ctx = qcom_spi_ecc_init_ctx_pipelined, ++ .cleanup_ctx = qcom_spi_ecc_cleanup_ctx_pipelined, ++ .prepare_io_req = qcom_spi_ecc_prepare_io_req_pipelined, ++ .finish_io_req = qcom_spi_ecc_finish_io_req_pipelined, ++}; ++ ++/* helper to configure location register values */ ++static void qcom_spi_set_read_loc(struct qcom_nand_controller *snandc, int cw, int reg, ++ int cw_offset, int read_size, int is_last_read_loc) ++{ ++ int reg_base = NAND_READ_LOCATION_0; ++ int num_cw = snandc->qspi->num_cw; ++ ++ if (cw == (num_cw - 1)) ++ reg_base = NAND_READ_LOCATION_LAST_CW_0; ++ ++ reg_base += reg * 4; ++ ++ if (cw == (num_cw - 1)) ++ return qcom_spi_set_read_loc_last(snandc, reg_base, cw_offset, ++ read_size, is_last_read_loc); ++ else ++ return qcom_spi_set_read_loc_first(snandc, reg_base, cw_offset, ++ read_size, is_last_read_loc); ++} ++ ++static void ++qcom_spi_config_cw_read(struct qcom_nand_controller *snandc, bool use_ecc, int cw) ++{ ++ __le32 *reg = &snandc->regs->read_location0; ++ int num_cw = snandc->qspi->num_cw; ++ ++ qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); ++ if (cw == (num_cw - 1)) { ++ reg = &snandc->regs->read_location_last0; ++ qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_LAST_CW_0, 4, ++ NAND_BAM_NEXT_SGL); ++ } ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 2, 0); ++ qcom_read_reg_dma(snandc, NAND_ERASED_CW_DETECT_STATUS, 1, ++ NAND_BAM_NEXT_SGL); ++} ++ ++static int qcom_spi_block_erase(struct qcom_nand_controller *snandc) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ int ret; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->addr0 = snandc->qspi->addr1; ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cfg0 = cpu_to_le32(ecc_cfg->cfg0_raw & ~(7 << CW_PER_PAGE)); ++ snandc->regs->cfg1 = cpu_to_le32(ecc_cfg->cfg1_raw); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to erase block\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void qcom_spi_config_single_cw_page_read(struct qcom_nand_controller *snandc, ++ bool use_ecc, int cw) ++{ ++ __le32 *reg = &snandc->regs->read_location0; ++ int num_cw = snandc->qspi->num_cw; ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, ++ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ ++ if (cw == (num_cw - 1)) { ++ reg = &snandc->regs->read_location_last0; ++ qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_LAST_CW_0, 4, NAND_BAM_NEXT_SGL); ++ } ++ qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 1, 0); ++} ++ ++static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ struct mtd_info *mtd = snandc->qspi->mtd; ++ int size, ret = 0; ++ int col, bbpos; ++ u32 cfg0, cfg1, ecc_bch_cfg; ++ u32 num_cw = snandc->qspi->num_cw; ++ ++ qcom_clear_bam_transaction(snandc); ++ qcom_clear_read_regs(snandc); ++ ++ size = ecc_cfg->cw_size; ++ col = ecc_cfg->cw_size * (num_cw - 1); ++ ++ memset(snandc->data_buffer, 0xff, size); ++ snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ ++ cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ 0 << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1_raw; ++ ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); ++ snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_set_read_loc(snandc, num_cw - 1, 0, 0, ecc_cfg->cw_size, 1); ++ ++ qcom_spi_config_single_cw_page_read(snandc, false, num_cw - 1); ++ ++ qcom_read_data_dma(snandc, FLASH_BUF_ACC, snandc->data_buffer, size, 0); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failed to read last cw\n"); ++ return ret; ++ } ++ ++ qcom_nandc_dev_to_mem(snandc, true); ++ u32 flash = le32_to_cpu(snandc->reg_read_buf[0]); ++ ++ if (flash & (FS_OP_ERR | FS_MPU_ERR)) ++ return -EIO; ++ ++ bbpos = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); ++ ++ if (snandc->data_buffer[bbpos] == 0xff) ++ snandc->data_buffer[bbpos + 1] = 0xff; ++ if (snandc->data_buffer[bbpos] != 0xff) ++ snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos]; ++ ++ memcpy(op->data.buf.in, snandc->data_buffer + bbpos, op->data.nbytes); ++ ++ return ret; ++} ++ ++static int qcom_spi_check_error(struct qcom_nand_controller *snandc, u8 *data_buf, u8 *oob_buf) ++{ ++ struct snandc_read_status *buf; ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ int i, num_cw = snandc->qspi->num_cw; ++ bool flash_op_err = false, erased; ++ unsigned int max_bitflips = 0; ++ unsigned int uncorrectable_cws = 0; ++ ++ snandc->qspi->ecc_stats.failed = 0; ++ snandc->qspi->ecc_stats.corrected = 0; ++ ++ qcom_nandc_dev_to_mem(snandc, true); ++ buf = (struct snandc_read_status *)snandc->reg_read_buf; ++ ++ for (i = 0; i < num_cw; i++, buf++) { ++ u32 flash, buffer, erased_cw; ++ int data_len, oob_len; ++ ++ if (i == (num_cw - 1)) { ++ data_len = NANDC_STEP_SIZE - ((num_cw - 1) << 2); ++ oob_len = num_cw << 2; ++ } else { ++ data_len = ecc_cfg->cw_data; ++ oob_len = 0; ++ } ++ ++ flash = le32_to_cpu(buf->snandc_flash); ++ buffer = le32_to_cpu(buf->snandc_buffer); ++ erased_cw = le32_to_cpu(buf->snandc_erased_cw); ++ ++ if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) { ++ if (ecc_cfg->bch_enabled) ++ erased = (erased_cw & ERASED_CW) == ERASED_CW; ++ else ++ erased = false; ++ ++ if (!erased) ++ uncorrectable_cws |= BIT(i); ++ ++ } else if (flash & (FS_OP_ERR | FS_MPU_ERR)) { ++ flash_op_err = true; ++ } else { ++ unsigned int stat; ++ ++ stat = buffer & BS_CORRECTABLE_ERR_MSK; ++ snandc->qspi->ecc_stats.corrected += stat; ++ max_bitflips = max(max_bitflips, stat); ++ } ++ ++ if (data_buf) ++ data_buf += data_len; ++ if (oob_buf) ++ oob_buf += oob_len + ecc_cfg->bytes; ++ } ++ ++ if (flash_op_err) ++ return -EIO; ++ ++ if (!uncorrectable_cws) ++ snandc->qspi->ecc_stats.bitflips = max_bitflips; ++ else ++ snandc->qspi->ecc_stats.failed++; ++ ++ return 0; ++} ++ ++static int qcom_spi_check_raw_flash_errors(struct qcom_nand_controller *snandc, int cw_cnt) ++{ ++ int i; ++ ++ qcom_nandc_dev_to_mem(snandc, true); ++ ++ for (i = 0; i < cw_cnt; i++) { ++ u32 flash = le32_to_cpu(snandc->reg_read_buf[i]); ++ ++ if (flash & (FS_OP_ERR | FS_MPU_ERR)) ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_read_cw_raw(struct qcom_nand_controller *snandc, u8 *data_buf, ++ u8 *oob_buf, int cw) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ struct mtd_info *mtd = snandc->qspi->mtd; ++ int data_size1, data_size2, oob_size1, oob_size2; ++ int ret, reg_off = FLASH_BUF_ACC, read_loc = 0; ++ int raw_cw = cw; ++ u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; ++ int col; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ raw_cw = num_cw - 1; ++ ++ cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ 0 << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1_raw; ++ ecc_bch_cfg = ECC_CFG_ECC_DISABLE; ++ ++ col = ecc_cfg->cw_size * cw; ++ ++ snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); ++ snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_set_read_loc(snandc, raw_cw, 0, 0, ecc_cfg->cw_size, 1); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, ++ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ ++ data_size1 = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); ++ oob_size1 = ecc_cfg->bbm_size; ++ ++ if (cw == (num_cw - 1)) { ++ data_size2 = NANDC_STEP_SIZE - data_size1 - ++ ((num_cw - 1) * 4); ++ oob_size2 = (num_cw * 4) + ecc_cfg->ecc_bytes_hw + ++ ecc_cfg->spare_bytes; ++ } else { ++ data_size2 = ecc_cfg->cw_data - data_size1; ++ oob_size2 = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; ++ } ++ ++ qcom_spi_set_read_loc(snandc, cw, 0, read_loc, data_size1, 0); ++ read_loc += data_size1; ++ ++ qcom_spi_set_read_loc(snandc, cw, 1, read_loc, oob_size1, 0); ++ read_loc += oob_size1; ++ ++ qcom_spi_set_read_loc(snandc, cw, 2, read_loc, data_size2, 0); ++ read_loc += data_size2; ++ ++ qcom_spi_set_read_loc(snandc, cw, 3, read_loc, oob_size2, 1); ++ ++ qcom_spi_config_cw_read(snandc, false, raw_cw); ++ ++ qcom_read_data_dma(snandc, reg_off, data_buf, data_size1, 0); ++ reg_off += data_size1; ++ ++ qcom_read_data_dma(snandc, reg_off, oob_buf, oob_size1, 0); ++ reg_off += oob_size1; ++ ++ qcom_read_data_dma(snandc, reg_off, data_buf + data_size1, data_size2, 0); ++ reg_off += data_size2; ++ ++ qcom_read_data_dma(snandc, reg_off, oob_buf + oob_size1, oob_size2, 0); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to read raw cw %d\n", cw); ++ return ret; ++ } ++ ++ return qcom_spi_check_raw_flash_errors(snandc, 1); ++} ++ ++static int qcom_spi_read_page_raw(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ u8 *data_buf = NULL, *oob_buf = NULL; ++ int ret, cw; ++ u32 num_cw = snandc->qspi->num_cw; ++ ++ if (snandc->qspi->page_rw) ++ data_buf = op->data.buf.in; ++ ++ oob_buf = snandc->qspi->oob_buf; ++ memset(oob_buf, 0xff, OOB_BUF_SIZE); ++ ++ for (cw = 0; cw < num_cw; cw++) { ++ ret = qcom_spi_read_cw_raw(snandc, data_buf, oob_buf, cw); ++ if (ret) ++ return ret; ++ ++ if (data_buf) ++ data_buf += ecc_cfg->cw_data; ++ if (oob_buf) ++ oob_buf += ecc_cfg->bytes; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ u8 *data_buf = NULL, *data_buf_start, *oob_buf = NULL, *oob_buf_start; ++ int ret, i; ++ u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; ++ ++ data_buf = op->data.buf.in; ++ data_buf_start = data_buf; ++ ++ oob_buf = snandc->qspi->oob_buf; ++ oob_buf_start = oob_buf; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ ++ cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1; ++ ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; ++ ++ snandc->regs->addr0 = snandc->qspi->addr1; ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); ++ snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_set_read_loc(snandc, 0, 0, 0, ecc_cfg->cw_data, 1); ++ ++ qcom_clear_bam_transaction(snandc); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, ++ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ ++ for (i = 0; i < num_cw; i++) { ++ int data_size, oob_size; ++ ++ if (i == (num_cw - 1)) { ++ data_size = 512 - ((num_cw - 1) << 2); ++ oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + ++ ecc_cfg->spare_bytes; ++ } else { ++ data_size = ecc_cfg->cw_data; ++ oob_size = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; ++ } ++ ++ if (data_buf && oob_buf) { ++ qcom_spi_set_read_loc(snandc, i, 0, 0, data_size, 0); ++ qcom_spi_set_read_loc(snandc, i, 1, data_size, oob_size, 1); ++ } else if (data_buf) { ++ qcom_spi_set_read_loc(snandc, i, 0, 0, data_size, 1); ++ } else { ++ qcom_spi_set_read_loc(snandc, i, 0, data_size, oob_size, 1); ++ } ++ ++ qcom_spi_config_cw_read(snandc, true, i); ++ ++ if (data_buf) ++ qcom_read_data_dma(snandc, FLASH_BUF_ACC, data_buf, ++ data_size, 0); ++ if (oob_buf) { ++ int j; ++ ++ for (j = 0; j < ecc_cfg->bbm_size; j++) ++ *oob_buf++ = 0xff; ++ ++ qcom_read_data_dma(snandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); ++ } ++ ++ if (data_buf) ++ data_buf += data_size; ++ if (oob_buf) ++ oob_buf += oob_size; ++ } ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to read page\n"); ++ return ret; ++ } ++ ++ return qcom_spi_check_error(snandc, data_buf_start, oob_buf_start); ++} ++ ++static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ u8 *data_buf = NULL, *data_buf_start, *oob_buf = NULL, *oob_buf_start; ++ int ret, i; ++ u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; ++ ++ oob_buf = op->data.buf.in; ++ oob_buf_start = oob_buf; ++ ++ data_buf_start = data_buf; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ ++ cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1; ++ ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; ++ ++ snandc->regs->addr0 = snandc->qspi->addr1; ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); ++ snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_set_read_loc(snandc, 0, 0, 0, ecc_cfg->cw_data, 1); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, ++ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ ++ for (i = 0; i < num_cw; i++) { ++ int data_size, oob_size; ++ ++ if (i == (num_cw - 1)) { ++ data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); ++ oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + ++ ecc_cfg->spare_bytes; ++ } else { ++ data_size = ecc_cfg->cw_data; ++ oob_size = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; ++ } ++ ++ qcom_spi_set_read_loc(snandc, i, 0, data_size, oob_size, 1); ++ ++ qcom_spi_config_cw_read(snandc, true, i); ++ ++ if (oob_buf) { ++ int j; ++ ++ for (j = 0; j < ecc_cfg->bbm_size; j++) ++ *oob_buf++ = 0xff; ++ ++ qcom_read_data_dma(snandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); ++ } ++ ++ if (oob_buf) ++ oob_buf += oob_size; ++ } ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to read oob\n"); ++ return ret; ++ } ++ ++ return qcom_spi_check_error(snandc, data_buf_start, oob_buf_start); ++} ++ ++static int qcom_spi_read_page(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ if (snandc->qspi->page_rw && snandc->qspi->raw_rw) ++ return qcom_spi_read_page_raw(snandc, op); ++ ++ if (snandc->qspi->page_rw) ++ return qcom_spi_read_page_ecc(snandc, op); ++ ++ if (snandc->qspi->oob_rw && snandc->qspi->raw_rw) ++ return qcom_spi_read_last_cw(snandc, op); ++ ++ if (snandc->qspi->oob_rw) ++ return qcom_spi_read_page_oob(snandc, op); ++ ++ return 0; ++} ++ ++static void qcom_spi_config_page_write(struct qcom_nand_controller *snandc) ++{ ++ qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, ++ 1, NAND_BAM_NEXT_SGL); ++} ++ ++static void qcom_spi_config_cw_write(struct qcom_nand_controller *snandc) ++{ ++ qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->clrreadstatus, NAND_READ_STATUS, 1, ++ NAND_BAM_NEXT_SGL); ++} ++ ++static int qcom_spi_program_raw(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ struct mtd_info *mtd = snandc->qspi->mtd; ++ u8 *data_buf = NULL, *oob_buf = NULL; ++ int i, ret; ++ int num_cw = snandc->qspi->num_cw; ++ u32 cfg0, cfg1, ecc_bch_cfg; ++ ++ cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1_raw; ++ ecc_bch_cfg = ECC_CFG_ECC_DISABLE; ++ ++ data_buf = snandc->qspi->data_buf; ++ ++ oob_buf = snandc->qspi->oob_buf; ++ memset(oob_buf, 0xff, OOB_BUF_SIZE); ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ ++ snandc->regs->addr0 = snandc->qspi->addr1; ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); ++ snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_config_page_write(snandc); ++ ++ for (i = 0; i < num_cw; i++) { ++ int data_size1, data_size2, oob_size1, oob_size2; ++ int reg_off = FLASH_BUF_ACC; ++ ++ data_size1 = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); ++ oob_size1 = ecc_cfg->bbm_size; ++ ++ if (i == (num_cw - 1)) { ++ data_size2 = NANDC_STEP_SIZE - data_size1 - ++ ((num_cw - 1) << 2); ++ oob_size2 = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + ++ ecc_cfg->spare_bytes; ++ } else { ++ data_size2 = ecc_cfg->cw_data - data_size1; ++ oob_size2 = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; ++ } ++ ++ qcom_write_data_dma(snandc, reg_off, data_buf, data_size1, ++ NAND_BAM_NO_EOT); ++ reg_off += data_size1; ++ data_buf += data_size1; ++ ++ qcom_write_data_dma(snandc, reg_off, oob_buf, oob_size1, ++ NAND_BAM_NO_EOT); ++ oob_buf += oob_size1; ++ reg_off += oob_size1; ++ ++ qcom_write_data_dma(snandc, reg_off, data_buf, data_size2, ++ NAND_BAM_NO_EOT); ++ reg_off += data_size2; ++ data_buf += data_size2; ++ ++ qcom_write_data_dma(snandc, reg_off, oob_buf, oob_size2, 0); ++ oob_buf += oob_size2; ++ ++ qcom_spi_config_cw_write(snandc); ++ } ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to write raw page\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_program_ecc(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ u8 *data_buf = NULL, *oob_buf = NULL; ++ int i, ret; ++ int num_cw = snandc->qspi->num_cw; ++ u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; ++ ++ cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1; ++ ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; ++ ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; ++ ++ if (snandc->qspi->data_buf) ++ data_buf = snandc->qspi->data_buf; ++ ++ oob_buf = snandc->qspi->oob_buf; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ ++ snandc->regs->addr0 = snandc->qspi->addr1; ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->ecc_buf_cfg = cpu_to_le32(ecc_buf_cfg); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_config_page_write(snandc); ++ ++ for (i = 0; i < num_cw; i++) { ++ int data_size, oob_size; ++ ++ if (i == (num_cw - 1)) { ++ data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); ++ oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + ++ ecc_cfg->spare_bytes; ++ } else { ++ data_size = ecc_cfg->cw_data; ++ oob_size = ecc_cfg->bytes; ++ } ++ ++ if (data_buf) ++ qcom_write_data_dma(snandc, FLASH_BUF_ACC, data_buf, data_size, ++ i == (num_cw - 1) ? NAND_BAM_NO_EOT : 0); ++ ++ if (i == (num_cw - 1)) { ++ if (oob_buf) { ++ oob_buf += ecc_cfg->bbm_size; ++ qcom_write_data_dma(snandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); ++ } ++ } ++ ++ qcom_spi_config_cw_write(snandc); ++ ++ if (data_buf) ++ data_buf += data_size; ++ if (oob_buf) ++ oob_buf += oob_size; ++ } ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to write page\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_program_oob(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ u8 *oob_buf = NULL; ++ int ret, col, data_size, oob_size; ++ int num_cw = snandc->qspi->num_cw; ++ u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; ++ ++ cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1; ++ ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; ++ ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; ++ ++ col = ecc_cfg->cw_size * (num_cw - 1); ++ ++ oob_buf = snandc->qspi->data_buf; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->ecc_buf_cfg = cpu_to_le32(ecc_buf_cfg); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ /* calculate the data and oob size for the last codeword/step */ ++ data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); ++ oob_size = snandc->qspi->mtd->oobavail; ++ ++ memset(snandc->data_buffer, 0xff, ecc_cfg->cw_data); ++ /* override new oob content to last codeword */ ++ mtd_ooblayout_get_databytes(snandc->qspi->mtd, snandc->data_buffer + data_size, ++ oob_buf, 0, snandc->qspi->mtd->oobavail); ++ qcom_spi_config_page_write(snandc); ++ qcom_write_data_dma(snandc, FLASH_BUF_ACC, snandc->data_buffer, data_size + oob_size, 0); ++ qcom_spi_config_cw_write(snandc); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to write oob\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_program_execute(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ if (snandc->qspi->page_rw && snandc->qspi->raw_rw) ++ return qcom_spi_program_raw(snandc, op); ++ ++ if (snandc->qspi->page_rw) ++ return qcom_spi_program_ecc(snandc, op); ++ ++ if (snandc->qspi->oob_rw) ++ return qcom_spi_program_oob(snandc, op); ++ ++ return 0; ++} ++ ++static int qcom_spi_cmd_mapping(struct qcom_nand_controller *snandc, u32 opcode, u32 *cmd) ++{ ++ switch (opcode) { ++ case SPINAND_RESET: ++ *cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_RESET_DEVICE); ++ break; ++ case SPINAND_READID: ++ *cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_FETCH_ID); ++ break; ++ case SPINAND_GET_FEATURE: ++ *cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE); ++ break; ++ case SPINAND_SET_FEATURE: ++ *cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE | ++ QPIC_SET_FEATURE); ++ break; ++ case SPINAND_READ: ++ if (snandc->qspi->raw_rw) { ++ *cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 | ++ SPI_WP | SPI_HOLD | OP_PAGE_READ); ++ } else { ++ *cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 | ++ SPI_WP | SPI_HOLD | OP_PAGE_READ_WITH_ECC); ++ } ++ ++ break; ++ case SPINAND_ERASE: ++ *cmd = OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE | SPI_WP | ++ SPI_HOLD | SPI_TRANSFER_MODE_x1; ++ break; ++ case SPINAND_WRITE_EN: ++ *cmd = SPINAND_WRITE_EN; ++ break; ++ case SPINAND_PROGRAM_EXECUTE: ++ *cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 | ++ SPI_WP | SPI_HOLD | OP_PROGRAM_PAGE); ++ break; ++ case SPINAND_PROGRAM_LOAD: ++ *cmd = SPINAND_PROGRAM_LOAD; ++ break; ++ default: ++ dev_err(snandc->dev, "Opcode not supported: %u\n", opcode); ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_write_page(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ int ret; ++ u32 cmd; ++ ++ ret = qcom_spi_cmd_mapping(snandc, op->cmd.opcode, &cmd); ++ if (ret < 0) ++ return ret; ++ ++ if (op->cmd.opcode == SPINAND_PROGRAM_LOAD) ++ snandc->qspi->data_buf = (u8 *)op->data.buf.out; ++ ++ return 0; ++} ++ ++static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_snand_op s_op = {}; ++ u32 cmd; ++ int ret, opcode; ++ ++ ret = qcom_spi_cmd_mapping(snandc, op->cmd.opcode, &cmd); ++ if (ret < 0) ++ return ret; ++ ++ s_op.cmd_reg = cmd; ++ s_op.addr1_reg = op->addr.val; ++ s_op.addr2_reg = 0; ++ ++ opcode = op->cmd.opcode; ++ ++ switch (opcode) { ++ case SPINAND_WRITE_EN: ++ return 0; ++ case SPINAND_PROGRAM_EXECUTE: ++ s_op.addr1_reg = op->addr.val << 16; ++ s_op.addr2_reg = op->addr.val >> 16 & 0xff; ++ snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg); ++ snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); ++ snandc->qspi->cmd = cpu_to_le32(cmd); ++ return qcom_spi_program_execute(snandc, op); ++ case SPINAND_READ: ++ s_op.addr1_reg = (op->addr.val << 16); ++ s_op.addr2_reg = op->addr.val >> 16 & 0xff; ++ snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg); ++ snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); ++ snandc->qspi->cmd = cpu_to_le32(cmd); ++ return 0; ++ case SPINAND_ERASE: ++ s_op.addr2_reg = (op->addr.val >> 16) & 0xffff; ++ s_op.addr1_reg = op->addr.val; ++ snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg << 16); ++ snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); ++ snandc->qspi->cmd = cpu_to_le32(cmd); ++ qcom_spi_block_erase(snandc); ++ return 0; ++ default: ++ break; ++ } ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ ++ snandc->regs->cmd = cpu_to_le32(s_op.cmd_reg); ++ snandc->regs->exec = cpu_to_le32(1); ++ snandc->regs->addr0 = cpu_to_le32(s_op.addr1_reg); ++ snandc->regs->addr1 = cpu_to_le32(s_op.addr2_reg); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) ++ dev_err(snandc->dev, "failure in submitting cmd descriptor\n"); ++ ++ return ret; ++} ++ ++static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_mem_op *op) ++{ ++ int ret, val, opcode; ++ bool copy = false, copy_ftr = false; ++ ++ ret = qcom_spi_send_cmdaddr(snandc, op); ++ if (ret) ++ return ret; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ opcode = op->cmd.opcode; ++ ++ switch (opcode) { ++ case SPINAND_READID: ++ snandc->buf_count = 4; ++ qcom_read_reg_dma(snandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); ++ copy = true; ++ break; ++ case SPINAND_GET_FEATURE: ++ snandc->buf_count = 4; ++ qcom_read_reg_dma(snandc, NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); ++ copy_ftr = true; ++ break; ++ case SPINAND_SET_FEATURE: ++ snandc->regs->flash_feature = cpu_to_le32(*(u32 *)op->data.buf.out); ++ qcom_write_reg_dma(snandc, &snandc->regs->flash_feature, ++ NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); ++ break; ++ case SPINAND_PROGRAM_EXECUTE: ++ case SPINAND_WRITE_EN: ++ case SPINAND_RESET: ++ case SPINAND_ERASE: ++ case SPINAND_READ: ++ return 0; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) ++ dev_err(snandc->dev, "failure in submitting descriptor for:%d\n", opcode); ++ ++ if (copy) { ++ qcom_nandc_dev_to_mem(snandc, true); ++ memcpy(op->data.buf.in, snandc->reg_read_buf, snandc->buf_count); ++ } ++ ++ if (copy_ftr) { ++ qcom_nandc_dev_to_mem(snandc, true); ++ val = le32_to_cpu(*(__le32 *)snandc->reg_read_buf); ++ val >>= 8; ++ memcpy(op->data.buf.in, &val, snandc->buf_count); ++ } ++ ++ return ret; ++} ++ ++static bool qcom_spi_is_page_op(const struct spi_mem_op *op) ++{ ++ if (op->addr.buswidth != 1 && op->addr.buswidth != 2 && op->addr.buswidth != 4) ++ return false; ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ if (op->addr.buswidth == 4 && op->data.buswidth == 4) ++ return true; ++ ++ if (op->addr.nbytes == 2 && op->addr.buswidth == 1) ++ return true; ++ ++ } else if (op->data.dir == SPI_MEM_DATA_OUT) { ++ if (op->data.buswidth == 4) ++ return true; ++ if (op->addr.nbytes == 2 && op->addr.buswidth == 1) ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool qcom_spi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) ++{ ++ if (!spi_mem_default_supports_op(mem, op)) ++ return false; ++ ++ if (op->cmd.nbytes != 1 || op->cmd.buswidth != 1) ++ return false; ++ ++ if (qcom_spi_is_page_op(op)) ++ return true; ++ ++ return ((!op->addr.nbytes || op->addr.buswidth == 1) && ++ (!op->dummy.nbytes || op->dummy.buswidth == 1) && ++ (!op->data.nbytes || op->data.buswidth == 1)); ++} ++ ++static int qcom_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) ++{ ++ struct qcom_nand_controller *snandc = spi_controller_get_devdata(mem->spi->controller); ++ ++ dev_dbg(snandc->dev, "OP %02x ADDR %08llX@%d:%u DATA %d:%u", op->cmd.opcode, ++ op->addr.val, op->addr.buswidth, op->addr.nbytes, ++ op->data.buswidth, op->data.nbytes); ++ ++ if (qcom_spi_is_page_op(op)) { ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ return qcom_spi_read_page(snandc, op); ++ if (op->data.dir == SPI_MEM_DATA_OUT) ++ return qcom_spi_write_page(snandc, op); ++ } else { ++ return qcom_spi_io_op(snandc, op); ++ } ++ ++ return 0; ++} ++ ++static const struct spi_controller_mem_ops qcom_spi_mem_ops = { ++ .supports_op = qcom_spi_supports_op, ++ .exec_op = qcom_spi_exec_op, ++}; ++ ++static const struct spi_controller_mem_caps qcom_spi_mem_caps = { ++ .ecc = true, ++}; ++ ++static int qcom_spi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct spi_controller *ctlr; ++ struct qcom_nand_controller *snandc; ++ struct qpic_spi_nand *qspi; ++ struct qpic_ecc *ecc; ++ struct resource *res; ++ const void *dev_data; ++ int ret; ++ ++ ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); ++ if (!ecc) ++ return -ENOMEM; ++ ++ qspi = devm_kzalloc(dev, sizeof(*qspi), GFP_KERNEL); ++ if (!qspi) ++ return -ENOMEM; ++ ++ ctlr = __devm_spi_alloc_controller(dev, sizeof(*snandc), false); ++ if (!ctlr) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, ctlr); ++ ++ snandc = spi_controller_get_devdata(ctlr); ++ qspi->snandc = snandc; ++ ++ snandc->dev = dev; ++ snandc->qspi = qspi; ++ snandc->qspi->ctlr = ctlr; ++ snandc->qspi->ecc = ecc; ++ ++ dev_data = of_device_get_match_data(dev); ++ if (!dev_data) { ++ dev_err(&pdev->dev, "failed to get device data\n"); ++ return -ENODEV; ++ } ++ ++ snandc->props = dev_data; ++ snandc->dev = &pdev->dev; ++ ++ snandc->core_clk = devm_clk_get(dev, "core"); ++ if (IS_ERR(snandc->core_clk)) ++ return PTR_ERR(snandc->core_clk); ++ ++ snandc->aon_clk = devm_clk_get(dev, "aon"); ++ if (IS_ERR(snandc->aon_clk)) ++ return PTR_ERR(snandc->aon_clk); ++ ++ snandc->qspi->iomacro_clk = devm_clk_get(dev, "iom"); ++ if (IS_ERR(snandc->qspi->iomacro_clk)) ++ return PTR_ERR(snandc->qspi->iomacro_clk); ++ ++ snandc->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(snandc->base)) ++ return PTR_ERR(snandc->base); ++ ++ snandc->base_phys = res->start; ++ snandc->base_dma = dma_map_resource(dev, res->start, resource_size(res), ++ DMA_BIDIRECTIONAL, 0); ++ if (dma_mapping_error(dev, snandc->base_dma)) ++ return -ENXIO; ++ ++ ret = clk_prepare_enable(snandc->core_clk); ++ if (ret) ++ goto err_dis_core_clk; ++ ++ ret = clk_prepare_enable(snandc->aon_clk); ++ if (ret) ++ goto err_dis_aon_clk; ++ ++ ret = clk_prepare_enable(snandc->qspi->iomacro_clk); ++ if (ret) ++ goto err_dis_iom_clk; ++ ++ ret = qcom_nandc_alloc(snandc); ++ if (ret) ++ goto err_snand_alloc; ++ ++ ret = qcom_spi_init(snandc); ++ if (ret) ++ goto err_spi_init; ++ ++ /* setup ECC engine */ ++ snandc->qspi->ecc_eng.dev = &pdev->dev; ++ snandc->qspi->ecc_eng.integration = NAND_ECC_ENGINE_INTEGRATION_PIPELINED; ++ snandc->qspi->ecc_eng.ops = &qcom_spi_ecc_engine_ops_pipelined; ++ snandc->qspi->ecc_eng.priv = snandc; ++ ++ ret = nand_ecc_register_on_host_hw_engine(&snandc->qspi->ecc_eng); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to register ecc engine:%d\n", ret); ++ goto err_spi_init; ++ } ++ ++ ctlr->num_chipselect = QPIC_QSPI_NUM_CS; ++ ctlr->mem_ops = &qcom_spi_mem_ops; ++ ctlr->mem_caps = &qcom_spi_mem_caps; ++ ctlr->dev.of_node = pdev->dev.of_node; ++ ctlr->mode_bits = SPI_TX_DUAL | SPI_RX_DUAL | ++ SPI_TX_QUAD | SPI_RX_QUAD; ++ ++ ret = spi_register_controller(ctlr); ++ if (ret) { ++ dev_err(&pdev->dev, "spi_register_controller failed.\n"); ++ goto err_spi_init; ++ } ++ ++ return 0; ++ ++err_spi_init: ++ qcom_nandc_unalloc(snandc); ++err_snand_alloc: ++ clk_disable_unprepare(snandc->qspi->iomacro_clk); ++err_dis_iom_clk: ++ clk_disable_unprepare(snandc->aon_clk); ++err_dis_aon_clk: ++ clk_disable_unprepare(snandc->core_clk); ++err_dis_core_clk: ++ dma_unmap_resource(dev, res->start, resource_size(res), ++ DMA_BIDIRECTIONAL, 0); ++ return ret; ++} ++ ++static void qcom_spi_remove(struct platform_device *pdev) ++{ ++ struct spi_controller *ctlr = platform_get_drvdata(pdev); ++ struct qcom_nand_controller *snandc = spi_controller_get_devdata(ctlr); ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ spi_unregister_controller(ctlr); ++ ++ qcom_nandc_unalloc(snandc); ++ ++ clk_disable_unprepare(snandc->aon_clk); ++ clk_disable_unprepare(snandc->core_clk); ++ clk_disable_unprepare(snandc->qspi->iomacro_clk); ++ ++ dma_unmap_resource(&pdev->dev, snandc->base_dma, resource_size(res), ++ DMA_BIDIRECTIONAL, 0); ++} ++ ++static const struct qcom_nandc_props ipq9574_snandc_props = { ++ .dev_cmd_reg_start = 0x7000, ++ .supports_bam = true, ++}; ++ ++static const struct of_device_id qcom_snandc_of_match[] = { ++ { ++ .compatible = "qcom,ipq9574-snand", ++ .data = &ipq9574_snandc_props, ++ }, ++ {} ++} ++MODULE_DEVICE_TABLE(of, qcom_snandc_of_match); ++ ++static struct platform_driver qcom_spi_driver = { ++ .driver = { ++ .name = "qcom_snand", ++ .of_match_table = qcom_snandc_of_match, ++ }, ++ .probe = qcom_spi_probe, ++ .remove_new = qcom_spi_remove, ++}; ++module_platform_driver(qcom_spi_driver); ++ ++MODULE_DESCRIPTION("SPI driver for QPIC QSPI cores"); ++MODULE_AUTHOR("Md Sadre Alam "); ++MODULE_LICENSE("GPL"); ++ +--- a/include/linux/mtd/nand-qpic-common.h ++++ b/include/linux/mtd/nand-qpic-common.h +@@ -325,6 +325,10 @@ struct nandc_regs { + __le32 read_location_last1; + __le32 read_location_last2; + __le32 read_location_last3; ++ __le32 spi_cfg; ++ __le32 num_addr_cycle; ++ __le32 busy_wait_cnt; ++ __le32 flash_feature; + + __le32 erased_cw_detect_cfg_clr; + __le32 erased_cw_detect_cfg_set; +@@ -339,6 +343,7 @@ struct nandc_regs { + * + * @core_clk: controller clock + * @aon_clk: another controller clock ++ * @iomacro_clk: io macro clock + * + * @regs: a contiguous chunk of memory for DMA register + * writes. contains the register values to be +@@ -348,6 +353,7 @@ struct nandc_regs { + * initialized via DT match data + * + * @controller: base controller structure ++ * @qspi: qpic spi structure + * @host_list: list containing all the chips attached to the + * controller + * +@@ -392,6 +398,7 @@ struct qcom_nand_controller { + const struct qcom_nandc_props *props; + + struct nand_controller *controller; ++ struct qpic_spi_nand *qspi; + struct list_head host_list; + + union { diff --git a/target/linux/generic/backport-6.12/410-08-v6.15-spi-spi-qpic-snand-Fix-ECC_CFG_ECC_DISABLE-shift-in-.patch b/target/linux/generic/backport-6.12/410-08-v6.15-spi-spi-qpic-snand-Fix-ECC_CFG_ECC_DISABLE-shift-in-.patch new file mode 100644 index 00000000000..ad43f8dff19 --- /dev/null +++ b/target/linux/generic/backport-6.12/410-08-v6.15-spi-spi-qpic-snand-Fix-ECC_CFG_ECC_DISABLE-shift-in-.patch @@ -0,0 +1,28 @@ +From cf1ba3cb245020459f2ca446b7a7b199839f5d83 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Thu, 6 Mar 2025 12:40:01 +0300 +Subject: [PATCH] spi: spi-qpic-snand: Fix ECC_CFG_ECC_DISABLE shift in + qcom_spi_read_last_cw() + +The ECC_CFG_ECC_DISABLE define is BIT(0). It's supposed to be used +directly instead of used as a shifter. + +Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Dan Carpenter +Link: https://patch.msgid.link/2f4b0a0b-2c03-41c0-8a4a-3d789a83832d@stanley.mountain +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -514,7 +514,7 @@ static int qcom_spi_read_last_cw(struct + cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | + 0 << CW_PER_PAGE; + cfg1 = ecc_cfg->cfg1_raw; +- ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ ecc_bch_cfg = ECC_CFG_ECC_DISABLE; + + snandc->regs->cmd = snandc->qspi->cmd; + snandc->regs->cfg0 = cpu_to_le32(cfg0); diff --git a/target/linux/generic/backport-6.12/410-09-v6.15-spi-spi-qpic-snand-avoid-memleak-in-qcom_spi_ecc_ini.patch b/target/linux/generic/backport-6.12/410-09-v6.15-spi-spi-qpic-snand-avoid-memleak-in-qcom_spi_ecc_ini.patch new file mode 100644 index 00000000000..1e2a3b0298b --- /dev/null +++ b/target/linux/generic/backport-6.12/410-09-v6.15-spi-spi-qpic-snand-avoid-memleak-in-qcom_spi_ecc_ini.patch @@ -0,0 +1,35 @@ +From d450cdd9c4398add1f2aa7200f2c95f1e3b9f9fa Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Thu, 13 Mar 2025 19:31:21 +0100 +Subject: [PATCH] spi: spi-qpic-snand: avoid memleak in + qcom_spi_ecc_init_ctx_pipelined() + +When the allocation of the OOB buffer fails, the +qcom_spi_ecc_init_ctx_pipelined() function returns without freeing +the memory allocated for 'ecc_cfg' thus it can cause a memory leak. + +Call kfree() to free 'ecc_cfg' before returning from the function +to avoid that. + +Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Gabor Juhos +Link: https://patch.msgid.link/20250313-qpic-snand-memleak-fix-v1-1-e54e78d1da3a@gmail.com +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -263,8 +263,10 @@ static int qcom_spi_ecc_init_ctx_pipelin + return -ENOMEM; + snandc->qspi->oob_buf = kzalloc(mtd->writesize + mtd->oobsize, + GFP_KERNEL); +- if (!snandc->qspi->oob_buf) ++ if (!snandc->qspi->oob_buf) { ++ kfree(ecc_cfg); + return -ENOMEM; ++ } + + memset(snandc->qspi->oob_buf, 0xff, mtd->writesize + mtd->oobsize); + diff --git a/target/linux/generic/backport-6.12/410-10-v6.15-spi-SPI_QPIC_SNAND-should-be-tristate-and-depend-on-.patch b/target/linux/generic/backport-6.12/410-10-v6.15-spi-SPI_QPIC_SNAND-should-be-tristate-and-depend-on-.patch new file mode 100644 index 00000000000..5e920484380 --- /dev/null +++ b/target/linux/generic/backport-6.12/410-10-v6.15-spi-SPI_QPIC_SNAND-should-be-tristate-and-depend-on-.patch @@ -0,0 +1,49 @@ +From d32c4e58545f17caaa854415f854691e32d42075 Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Wed, 26 Mar 2025 15:22:19 +0100 +Subject: [PATCH] spi: SPI_QPIC_SNAND should be tristate and depend on MTD + +SPI_QPIC_SNAND is the only driver that selects MTD instead of depending +on it, which could lead to circular dependencies. Moreover, as +SPI_QPIC_SNAND is bool, this forces MTD (and various related symbols) to +be built-in, as can be seen in an allmodconfig kernel. + +Except for a missing semicolon, there is no reason why SPI_QPIC_SNAND +cannot be tristate; all MODULE_*() boilerplate is already present. +Hence make SPI_QPIC_SNAND tristate, let it depend on MTD, and add the +missing semicolon. + +Fixes: 7304d1909080ef0c ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Geert Uytterhoeven +Link: https://patch.msgid.link/b63db431cbf35223a4400e44c296293d32c4543c.1742998909.git.geert+renesas@glider.be +Signed-off-by: Mark Brown +--- + drivers/spi/Kconfig | 4 ++-- + drivers/spi/spi-qpic-snand.c | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -899,9 +899,9 @@ config SPI_QCOM_QSPI + QSPI(Quad SPI) driver for Qualcomm QSPI controller. + + config SPI_QPIC_SNAND +- bool "QPIC SNAND controller" ++ tristate "QPIC SNAND controller" + depends on ARCH_QCOM || COMPILE_TEST +- select MTD ++ depends on MTD + help + QPIC_SNAND (QPIC SPI NAND) driver for Qualcomm QPIC controller. + QPIC controller supports both parallel nand and serial nand. +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -1614,7 +1614,7 @@ static const struct of_device_id qcom_sn + .data = &ipq9574_snandc_props, + }, + {} +-} ++}; + MODULE_DEVICE_TABLE(of, qcom_snandc_of_match); + + static struct platform_driver qcom_spi_driver = { diff --git a/target/linux/generic/backport-6.12/410-11-v6.15-spi-spi-qpic-snand-propagate-errors-from-qcom_spi_block_erase.patch b/target/linux/generic/backport-6.12/410-11-v6.15-spi-spi-qpic-snand-propagate-errors-from-qcom_spi_block_erase.patch new file mode 100644 index 00000000000..9da531bed13 --- /dev/null +++ b/target/linux/generic/backport-6.12/410-11-v6.15-spi-spi-qpic-snand-propagate-errors-from-qcom_spi_block_erase.patch @@ -0,0 +1,36 @@ +From: Gabor Juhos +Date: Wed, 23 Apr 2025 21:31:57 +0200 +Subject: [PATCH] spi: spi-qpic-snand: propagate errors from + qcom_spi_block_erase() + +The qcom_spi_block_erase() function returns with error in case of +failure. Change the qcom_spi_send_cmdaddr() function to propagate +these errors to the callers instead of returning with success. + +Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Gabor Juhos +Reviewed-by: Abel Vesa +Reviewed-by: Md Sadre Alam +--- + drivers/spi/spi-qpic-snand.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + + +--- +base-commit: 9c32cda43eb78f78c73aee4aa344b777714e259b +change-id: 20250422-qpic-snand-propagate-error-9c95811ab811 + +Best regards, + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -1307,8 +1307,7 @@ static int qcom_spi_send_cmdaddr(struct + snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg << 16); + snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); + snandc->qspi->cmd = cpu_to_le32(cmd); +- qcom_spi_block_erase(snandc); +- return 0; ++ return qcom_spi_block_erase(snandc); + default: + break; + } diff --git a/target/linux/generic/backport-6.12/410-12-v6.15-spi-spi-qpic-snand-fix-NAND_READ_LOCATION_2-register-handling.patch b/target/linux/generic/backport-6.12/410-12-v6.15-spi-spi-qpic-snand-fix-NAND_READ_LOCATION_2-register-handling.patch new file mode 100644 index 00000000000..a2cd636fee4 --- /dev/null +++ b/target/linux/generic/backport-6.12/410-12-v6.15-spi-spi-qpic-snand-fix-NAND_READ_LOCATION_2-register-handling.patch @@ -0,0 +1,35 @@ +From: Gabor Juhos +Date: Mon, 28 Apr 2025 09:30:55 +0200 +Subject: [PATCH] spi: spi-qpic-snand: fix NAND_READ_LOCATION_2 register + handling + +The precomputed value for the NAND_READ_LOCATION_2 register should be +stored in 'snandc->regs->read_location2'. + +Fix the qcom_spi_set_read_loc_first() function accordingly. + +Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Gabor Juhos +Reviewed-by: Md Sadre Alam +--- + drivers/spi/spi-qpic-snand.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + + +--- +base-commit: 15cfe55ec58ace931a73e19e5367598734ceb074 +change-id: 20250428-qpic-snand-readloc2-fix-bccd07cf26d3 + +Best regards, + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -142,7 +142,7 @@ static void qcom_spi_set_read_loc_first( + else if (reg == NAND_READ_LOCATION_1) + snandc->regs->read_location1 = locreg_val; + else if (reg == NAND_READ_LOCATION_2) +- snandc->regs->read_location1 = locreg_val; ++ snandc->regs->read_location2 = locreg_val; + else if (reg == NAND_READ_LOCATION_3) + snandc->regs->read_location3 = locreg_val; + } diff --git a/target/linux/generic/backport-6.12/410-13-v6.15-spi-spi-qpic-snand-use-kmalloc-for-OOB-buffer-alloca.patch b/target/linux/generic/backport-6.12/410-13-v6.15-spi-spi-qpic-snand-use-kmalloc-for-OOB-buffer-alloca.patch new file mode 100644 index 00000000000..5a3d498bcbb --- /dev/null +++ b/target/linux/generic/backport-6.12/410-13-v6.15-spi-spi-qpic-snand-use-kmalloc-for-OOB-buffer-alloca.patch @@ -0,0 +1,29 @@ +From f48d80503504257682e493dc17408f2f0b47bcfa Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Thu, 20 Mar 2025 19:11:59 +0100 +Subject: [PATCH] spi: spi-qpic-snand: use kmalloc() for OOB buffer allocation + +The qcom_spi_ecc_init_ctx_pipelined() function allocates zeroed +memory for the OOB buffer, then it fills the buffer with '0xff' +bytes right after the allocation. In this case zeroing the memory +during allocation is superfluous, so use kmalloc() instead of +kzalloc() to avoid that. + +Signed-off-by: Gabor Juhos +Link: https://patch.msgid.link/20250320-qpic-snand-kmalloc-v1-1-94e267550675@gmail.com +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -261,7 +261,7 @@ static int qcom_spi_ecc_init_ctx_pipelin + ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL); + if (!ecc_cfg) + return -ENOMEM; +- snandc->qspi->oob_buf = kzalloc(mtd->writesize + mtd->oobsize, ++ snandc->qspi->oob_buf = kmalloc(mtd->writesize + mtd->oobsize, + GFP_KERNEL); + if (!snandc->qspi->oob_buf) { + kfree(ecc_cfg); diff --git a/target/linux/generic/backport-6.12/410-14-v6.16-spi-spi-qpic-snand-remove-unused-wlen-member-of-struct-qpic_spi_nand.patch b/target/linux/generic/backport-6.12/410-14-v6.16-spi-spi-qpic-snand-remove-unused-wlen-member-of-struct-qpic_spi_nand.patch new file mode 100644 index 00000000000..55e84daf09a --- /dev/null +++ b/target/linux/generic/backport-6.12/410-14-v6.16-spi-spi-qpic-snand-remove-unused-wlen-member-of-struct-qpic_spi_nand.patch @@ -0,0 +1,30 @@ +From: Gabor Juhos +Date: Thu, 24 Apr 2025 20:10:59 +0200 +Subject: [PATCH] spi: spi-qpic-snand: remove unused 'wlen' member of + 'struct qpic_spi_nand' + +The 'wlen' member of the qpic_spi_nand structure is never used in the +code so remove that. + +Signed-off-by: Gabor Juhos +--- + drivers/spi/spi-qpic-snand.c | 1 - + 1 file changed, 1 deletion(-) + + +--- +base-commit: 9c32cda43eb78f78c73aee4aa344b777714e259b +change-id: 20250424-qpic-snand-remove-wlen-c0cef3801a7f + +Best regards, + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -116,7 +116,6 @@ struct qpic_spi_nand { + struct nand_ecc_engine ecc_eng; + u8 *data_buf; + u8 *oob_buf; +- u32 wlen; + __le32 addr1; + __le32 addr2; + __le32 cmd; diff --git a/target/linux/generic/backport-6.12/411-v6.15-mtd-nand-Drop-explicit-test-for-built-in-CONFIG_SPI_.patch b/target/linux/generic/backport-6.12/411-v6.15-mtd-nand-Drop-explicit-test-for-built-in-CONFIG_SPI_.patch new file mode 100644 index 00000000000..8019d2ccaca --- /dev/null +++ b/target/linux/generic/backport-6.12/411-v6.15-mtd-nand-Drop-explicit-test-for-built-in-CONFIG_SPI_.patch @@ -0,0 +1,37 @@ +From 36c6468724aa98d33fea9a1d7e07ddda6302f5d4 Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Fri, 28 Mar 2025 09:24:01 +0100 +Subject: mtd: nand: Drop explicit test for built-in CONFIG_SPI_QPIC_SNAND + +If CONFIG_SPI_QPIC_SNAND=m, but CONFIG_MTD_NAND_QCOM=n: + + ERROR: modpost: "qcom_nandc_unalloc" [drivers/spi/spi-qpic-snand.ko] undefined! + ... + +Fix this by dropping the explicit test for a built-in +CONFIG_SPI_QPIC_SNAND completely. Kbuild handles multiple and mixed +obj-y/obj-m rules for the same object file fine. + +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202503280759.XhwLcV7m-lkp@intel.com/ +Fixes: 7304d1909080ef0c ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Geert Uytterhoeven +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/Makefile | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -3,11 +3,8 @@ + nandcore-objs := core.o bbt.o + obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o + obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o +-ifeq ($(CONFIG_SPI_QPIC_SNAND),y) + obj-$(CONFIG_SPI_QPIC_SNAND) += qpic_common.o +-else + obj-$(CONFIG_MTD_NAND_QCOM) += qpic_common.o +-endif + obj-y += onenand/ + obj-y += raw/ + obj-y += spi/ diff --git a/target/linux/generic/backport-6.12/412-v6.16-next-spi-spi-qpic-snand-validate-user-chip-specific-ECC-properties.patch b/target/linux/generic/backport-6.12/412-v6.16-next-spi-spi-qpic-snand-validate-user-chip-specific-ECC-properties.patch new file mode 100644 index 00000000000..2f951564c19 --- /dev/null +++ b/target/linux/generic/backport-6.12/412-v6.16-next-spi-spi-qpic-snand-validate-user-chip-specific-ECC-properties.patch @@ -0,0 +1,166 @@ +From 65cb56d49f6edea409600a3c61effc70ee5d43d8 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Thu, 1 May 2025 18:19:16 +0200 +Subject: spi: spi-qpic-snand: validate user/chip specific ECC properties + +The driver only supports 512 bytes ECC step size and 4 bit ECC strength +at the moment, however it does not reject unsupported step/strength +configurations. Due to this, whenever the driver is used with a flash +chip which needs stronger ECC protection, the following warning is shown +in the kernel log: + + [ 0.574648] spi-nand spi0.0: GigaDevice SPI NAND was found. + [ 0.635748] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128 + [ 0.649079] nand: WARNING: (null): the ECC used on your system is too weak compared to the one required by the NAND chip + +Although the message indicates that something is wrong, but it often gets +unnoticed, which can cause serious problems. For example when the user +writes something into the flash chip despite the warning, the written data +may won't be readable by the bootloader or by the boot ROM. In the worst +case, when the attached SPI NAND chip is the boot device, the board may not +be able to boot anymore. + +Also, it is not even possible to create a backup of the flash, because +reading its content results in bogus data. For example, dumping the first +page of the flash gives this: + + # hexdump -C -n 2048 /dev/mtd0 + 00000000 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 00000040 0f 0f 0f 0f 0f 0f 0f 0d 0f 0f 0f 0f 0f 0f 0f 0f |................| + 00000050 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 000001c0 0f 0f 0f 0f ff 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + 000001d0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 00000200 0f 0f 0f 0f f5 5b ff ff 0f 0f 0f 0f 0f 0f 0f 0f |.....[..........| + 00000210 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 000002f0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 1f 0f 0f |................| + 00000300 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 000003c0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ff 0f 0f 0f |................| + 000003d0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 00000400 0f 0f 0f 0f 0f 0f 0f 0f e9 74 c9 06 f5 5b ff ff |.........t...[..| + 00000410 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 000005d0 0f 0f 0f 0f ff 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + 000005e0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 00000600 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f c6 be 0f c3 |................| + 00000610 e9 74 c9 06 f5 5b ff ff 0f 0f 0f 0f 0f 0f 0f 0f |.t...[..........| + 00000620 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 00000770 0f 0f 0f 0f 8f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + 00000780 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 00000800 + # + +Doing the same by using the downstream kernel results in different output: + + # hexdump -C -n 2048 /dev/mtd0 + 00000000 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| + * + 00000800 + # + +This patch adds some sanity checks to the code to prevent using the driver +with unsupported ECC step/strength configurations. After the change, probing +of the driver fails in such cases: + + [ 0.655038] spi-nand spi0.0: GigaDevice SPI NAND was found. + [ 0.659159] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128 + [ 0.669138] qcom_snand 79b0000.spi: only 4 bits ECC strength is supported + [ 0.677476] nand: No suitable ECC configuration + [ 0.689909] spi-nand spi0.0: probe with driver spi-nand failed with error -95 + +This helps to avoid the aforementioned hassles until support for 8 bit ECC +strength gets implemented. + +Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Gabor Juhos +Link: https://patch.msgid.link/20250501-qpic-snand-validate-ecc-v1-1-532776581a66@gmail.com +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 42 +++++++++++++++++++++++++++++++----- + 1 file changed, 37 insertions(+), 5 deletions(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -249,9 +249,11 @@ static const struct mtd_ooblayout_ops qc + static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) + { + struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct nand_ecc_props *reqs = &nand->ecc.requirements; ++ struct nand_ecc_props *user = &nand->ecc.user_conf; + struct nand_ecc_props *conf = &nand->ecc.ctx.conf; + struct mtd_info *mtd = nanddev_to_mtd(nand); +- int cwperpage, bad_block_byte; ++ int cwperpage, bad_block_byte, ret; + struct qpic_ecc *ecc_cfg; + + cwperpage = mtd->writesize / NANDC_STEP_SIZE; +@@ -260,11 +262,39 @@ static int qcom_spi_ecc_init_ctx_pipelin + ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL); + if (!ecc_cfg) + return -ENOMEM; ++ ++ if (user->step_size && user->strength) { ++ ecc_cfg->step_size = user->step_size; ++ ecc_cfg->strength = user->strength; ++ } else if (reqs->step_size && reqs->strength) { ++ ecc_cfg->step_size = reqs->step_size; ++ ecc_cfg->strength = reqs->strength; ++ } else { ++ /* use defaults */ ++ ecc_cfg->step_size = NANDC_STEP_SIZE; ++ ecc_cfg->strength = 4; ++ } ++ ++ if (ecc_cfg->step_size != NANDC_STEP_SIZE) { ++ dev_err(snandc->dev, ++ "only %u bytes ECC step size is supported\n", ++ NANDC_STEP_SIZE); ++ ret = -EOPNOTSUPP; ++ goto err_free_ecc_cfg; ++ } ++ ++ if (ecc_cfg->strength != 4) { ++ dev_err(snandc->dev, ++ "only 4 bits ECC strength is supported\n"); ++ ret = -EOPNOTSUPP; ++ goto err_free_ecc_cfg; ++ } ++ + snandc->qspi->oob_buf = kmalloc(mtd->writesize + mtd->oobsize, + GFP_KERNEL); + if (!snandc->qspi->oob_buf) { +- kfree(ecc_cfg); +- return -ENOMEM; ++ ret = -ENOMEM; ++ goto err_free_ecc_cfg; + } + + memset(snandc->qspi->oob_buf, 0xff, mtd->writesize + mtd->oobsize); +@@ -279,8 +309,6 @@ static int qcom_spi_ecc_init_ctx_pipelin + ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size; + + ecc_cfg->steps = 4; +- ecc_cfg->strength = 4; +- ecc_cfg->step_size = 512; + ecc_cfg->cw_data = 516; + ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes; + bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1; +@@ -338,6 +366,10 @@ static int qcom_spi_ecc_init_ctx_pipelin + ecc_cfg->strength, ecc_cfg->step_size); + + return 0; ++ ++err_free_ecc_cfg: ++ kfree(ecc_cfg); ++ return ret; + } + + static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand) diff --git a/target/linux/generic/backport-6.12/413-v6.16-spi-spi-qpic-snand-use-CW_PER_PAGE_MASK-bitmask.patch b/target/linux/generic/backport-6.12/413-v6.16-spi-spi-qpic-snand-use-CW_PER_PAGE_MASK-bitmask.patch new file mode 100644 index 00000000000..10c1651b3b1 --- /dev/null +++ b/target/linux/generic/backport-6.12/413-v6.16-spi-spi-qpic-snand-use-CW_PER_PAGE_MASK-bitmask.patch @@ -0,0 +1,113 @@ +From 2abf107dcd797c60c86e9f17319cd1658862f6b2 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Thu, 15 May 2025 20:58:05 +0200 +Subject: spi: spi-qpic-snand: use CW_PER_PAGE_MASK bitmask + +Change the code to use the already defined CW_PER_PAGE_MASK +bitmask along with the FIELD_PREP() macro instead of using +magic values. + +This makes the code more readable. It also syncs the affected +codes with their counterparts in the 'qcom_nandc' driver, so it +makes it easier to spot the differences between the two +implementations. + +No functional changes intended. + +Signed-off-by: Gabor Juhos +Reviewed-by: Md Sadre Alam +Link: https://patch.msgid.link/20250515-qpic-snand-use-bitmasks-v1-1-11729aeae73b@gmail.com +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 31 ++++++++++++++++--------------- + 1 file changed, 16 insertions(+), 15 deletions(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -483,7 +483,8 @@ static int qcom_spi_block_erase(struct q + snandc->regs->cmd = snandc->qspi->cmd; + snandc->regs->addr0 = snandc->qspi->addr1; + snandc->regs->addr1 = snandc->qspi->addr2; +- snandc->regs->cfg0 = cpu_to_le32(ecc_cfg->cfg0_raw & ~(7 << CW_PER_PAGE)); ++ snandc->regs->cfg0 = cpu_to_le32((ecc_cfg->cfg0_raw & ~CW_PER_PAGE_MASK) | ++ FIELD_PREP(CW_PER_PAGE_MASK, 0)); + snandc->regs->cfg1 = cpu_to_le32(ecc_cfg->cfg1_raw); + snandc->regs->exec = cpu_to_le32(1); + +@@ -544,8 +545,8 @@ static int qcom_spi_read_last_cw(struct + snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); + snandc->regs->addr1 = snandc->qspi->addr2; + +- cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | +- 0 << CW_PER_PAGE; ++ cfg0 = (ecc_cfg->cfg0_raw & ~CW_PER_PAGE_MASK) | ++ FIELD_PREP(CW_PER_PAGE_MASK, 0); + cfg1 = ecc_cfg->cfg1_raw; + ecc_bch_cfg = ECC_CFG_ECC_DISABLE; + +@@ -687,8 +688,8 @@ static int qcom_spi_read_cw_raw(struct q + qcom_clear_bam_transaction(snandc); + raw_cw = num_cw - 1; + +- cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | +- 0 << CW_PER_PAGE; ++ cfg0 = (ecc_cfg->cfg0_raw & ~CW_PER_PAGE_MASK) | ++ FIELD_PREP(CW_PER_PAGE_MASK, 0); + cfg1 = ecc_cfg->cfg1_raw; + ecc_bch_cfg = ECC_CFG_ECC_DISABLE; + +@@ -808,8 +809,8 @@ static int qcom_spi_read_page_ecc(struct + snandc->buf_start = 0; + qcom_clear_read_regs(snandc); + +- cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | +- (num_cw - 1) << CW_PER_PAGE; ++ cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) | ++ FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); + cfg1 = ecc_cfg->cfg1; + ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; + +@@ -904,8 +905,8 @@ static int qcom_spi_read_page_oob(struct + qcom_clear_read_regs(snandc); + qcom_clear_bam_transaction(snandc); + +- cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | +- (num_cw - 1) << CW_PER_PAGE; ++ cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) | ++ FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); + cfg1 = ecc_cfg->cfg1; + ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; + +@@ -1015,8 +1016,8 @@ static int qcom_spi_program_raw(struct q + int num_cw = snandc->qspi->num_cw; + u32 cfg0, cfg1, ecc_bch_cfg; + +- cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | +- (num_cw - 1) << CW_PER_PAGE; ++ cfg0 = (ecc_cfg->cfg0_raw & ~CW_PER_PAGE_MASK) | ++ FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); + cfg1 = ecc_cfg->cfg1_raw; + ecc_bch_cfg = ECC_CFG_ECC_DISABLE; + +@@ -1098,8 +1099,8 @@ static int qcom_spi_program_ecc(struct q + int num_cw = snandc->qspi->num_cw; + u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; + +- cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | +- (num_cw - 1) << CW_PER_PAGE; ++ cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) | ++ FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); + cfg1 = ecc_cfg->cfg1; + ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; + ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; +@@ -1175,8 +1176,8 @@ static int qcom_spi_program_oob(struct q + int num_cw = snandc->qspi->num_cw; + u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; + +- cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | +- (num_cw - 1) << CW_PER_PAGE; ++ cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) | ++ FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); + cfg1 = ecc_cfg->cfg1; + ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; + ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; diff --git a/target/linux/generic/backport-6.12/414-v6.16-spi-spi-qpic-snand-reallocate-BAM-transactions.patch b/target/linux/generic/backport-6.12/414-v6.16-spi-spi-qpic-snand-reallocate-BAM-transactions.patch new file mode 100644 index 00000000000..6d610165566 --- /dev/null +++ b/target/linux/generic/backport-6.12/414-v6.16-spi-spi-qpic-snand-reallocate-BAM-transactions.patch @@ -0,0 +1,81 @@ +From d85d0380292a7e618915069c3579ae23c7c80339 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Wed, 18 Jun 2025 22:22:49 +0200 +Subject: spi: spi-qpic-snand: reallocate BAM transactions + +Using the mtd_nandbiterrs module for testing the driver occasionally +results in weird things like below. + +1. swiotlb mapping fails with the following message: + + [ 85.926216] qcom_snand 79b0000.spi: swiotlb buffer is full (sz: 4294967294 bytes), total 512 (slots), used 0 (slots) + [ 85.932937] qcom_snand 79b0000.spi: failure in mapping desc + [ 87.999314] qcom_snand 79b0000.spi: failure to write raw page + [ 87.999352] mtd_nandbiterrs: error: write_oob failed (-110) + + Rebooting the board after this causes a panic due to a NULL pointer + dereference. + +2. If the swiotlb mapping does not fail, rebooting the board may result + in a different panic due to a bad spinlock magic: + + [ 256.104459] BUG: spinlock bad magic on CPU#3, procd/2241 + [ 256.104488] Unable to handle kernel paging request at virtual address ffffffff0000049b + ... + +Investigating the issue revealed that these symptoms are results of +memory corruption which is caused by out of bounds access within the +driver. + +The driver uses a dynamically allocated structure for BAM transactions, +which structure must have enough space for all possible variations of +different flash operations initiated by the driver. The required space +heavily depends on the actual number of 'codewords' which is calculated +from the pagesize of the actual NAND chip. + +Although the qcom_nandc_alloc() function allocates memory for the BAM +transactions during probe, but since the actual number of 'codewords' +is not yet know the allocation is done for one 'codeword' only. + +Because of this, whenever the driver does a flash operation, and the +number of the required transactions exceeds the size of the allocated +arrays the driver accesses memory out of the allocated range. + +To avoid this, change the code to free the initially allocated BAM +transactions memory, and allocate a new one once the actual number of +'codewords' required for a given NAND chip is known. + +Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Reviewed-by: Md Sadre Alam +Signed-off-by: Gabor Juhos +Link: https://patch.msgid.link/20250618-qpic-snand-avoid-mem-corruption-v3-1-319c71296cda@gmail.com +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -315,6 +315,22 @@ static int qcom_spi_ecc_init_ctx_pipelin + + mtd_set_ooblayout(mtd, &qcom_spi_ooblayout); + ++ /* ++ * Free the temporary BAM transaction allocated initially by ++ * qcom_nandc_alloc(), and allocate a new one based on the ++ * updated max_cwperpage value. ++ */ ++ qcom_free_bam_transaction(snandc); ++ ++ snandc->max_cwperpage = cwperpage; ++ ++ snandc->bam_txn = qcom_alloc_bam_transaction(snandc); ++ if (!snandc->bam_txn) { ++ dev_err(snandc->dev, "failed to allocate BAM transaction\n"); ++ ret = -ENOMEM; ++ goto err_free_ecc_cfg; ++ } ++ + ecc_cfg->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | + FIELD_PREP(UD_SIZE_BYTES_MASK, ecc_cfg->cw_data) | + FIELD_PREP(DISABLE_STATUS_AFTER_WRITE, 1) | diff --git a/target/linux/generic/backport-6.12/415-v6.16-spi-spi-qpic-snand-don-t-hardcode-ECC-steps.patch b/target/linux/generic/backport-6.12/415-v6.16-spi-spi-qpic-snand-don-t-hardcode-ECC-steps.patch new file mode 100644 index 00000000000..eab7b792aad --- /dev/null +++ b/target/linux/generic/backport-6.12/415-v6.16-spi-spi-qpic-snand-don-t-hardcode-ECC-steps.patch @@ -0,0 +1,31 @@ +From f820034864dd463cdcd2bebe7940f2eca0eb4223 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Wed, 23 Jul 2025 10:06:43 +0200 +Subject: spi: spi-qpic-snand: don't hardcode ECC steps + +NAND devices with different page sizes requires different number +of ECC steps, yet the qcom_spi_ecc_init_ctx_pipelined() function +sets 4 steps in 'ecc_cfg' unconditionally. + +The correct number of the steps is calculated earlier in the +function already, so use that instead of the hardcoded value. + +Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Gabor Juhos +Link: https://patch.msgid.link/20250723-qpic-snand-fix-steps-v1-1-d800695dde4c@gmail.com +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -308,7 +308,7 @@ static int qcom_spi_ecc_init_ctx_pipelin + ecc_cfg->bch_enabled = true; + ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size; + +- ecc_cfg->steps = 4; ++ ecc_cfg->steps = cwperpage; + ecc_cfg->cw_data = 516; + ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes; + bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1; diff --git a/target/linux/generic/backport-6.12/416-v6.17-mtd-nand-qpic-common-add-defines-for-ECC_MODE-values.patch b/target/linux/generic/backport-6.12/416-v6.17-mtd-nand-qpic-common-add-defines-for-ECC_MODE-values.patch new file mode 100644 index 00000000000..7ddf15ef706 --- /dev/null +++ b/target/linux/generic/backport-6.12/416-v6.17-mtd-nand-qpic-common-add-defines-for-ECC_MODE-values.patch @@ -0,0 +1,74 @@ +From 0dc7e656ddd54c3267b7cc18c1ac8ec1297ed02f Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Wed, 2 Jul 2025 14:35:23 +0200 +Subject: mtd: nand: qpic-common: add defines for ECC_MODE values + +Add defines for the values of the ECC_MODE field of the NAND_DEV0_ECC_CFG +register and change both the 'qcom-nandc' and 'spi-qpic-snand' drivers to +use those instead of magic numbers. + +No functional changes. This is in preparation for adding 8 bit ECC strength +support for the 'spi-qpic-snand' driver. + +Reviewed-by: Md Sadre Alam +Signed-off-by: Gabor Juhos +Acked-by: Miquel Raynal +Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-1-ae2c17a30bb7@gmail.com +Signed-off-by: Mark Brown +--- + drivers/mtd/nand/raw/qcom_nandc.c | 6 +++--- + drivers/spi/spi-qpic-snand.c | 2 +- + include/linux/mtd/nand-qpic-common.h | 2 ++ + 3 files changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -1379,7 +1379,7 @@ static int qcom_nand_attach_chip(struct + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int cwperpage, bad_block_byte, ret; + bool wide_bus; +- int ecc_mode = 1; ++ int ecc_mode = ECC_MODE_8BIT; + + /* controller only supports 512 bytes data steps */ + ecc->size = NANDC_STEP_SIZE; +@@ -1400,7 +1400,7 @@ static int qcom_nand_attach_chip(struct + if (ecc->strength >= 8) { + /* 8 bit ECC defaults to BCH ECC on all platforms */ + host->bch_enabled = true; +- ecc_mode = 1; ++ ecc_mode = ECC_MODE_8BIT; + + if (wide_bus) { + host->ecc_bytes_hw = 14; +@@ -1420,7 +1420,7 @@ static int qcom_nand_attach_chip(struct + if (nandc->props->ecc_modes & ECC_BCH_4BIT) { + /* BCH */ + host->bch_enabled = true; +- ecc_mode = 0; ++ ecc_mode = ECC_MODE_4BIT; + + if (wide_bus) { + host->ecc_bytes_hw = 8; +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -365,7 +365,7 @@ static int qcom_spi_ecc_init_ctx_pipelin + FIELD_PREP(ECC_SW_RESET, 0) | + FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) | + FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | +- FIELD_PREP(ECC_MODE_MASK, 0) | ++ FIELD_PREP(ECC_MODE_MASK, ECC_MODE_4BIT) | + FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); + + ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS; +--- a/include/linux/mtd/nand-qpic-common.h ++++ b/include/linux/mtd/nand-qpic-common.h +@@ -101,6 +101,8 @@ + #define ECC_SW_RESET BIT(1) + #define ECC_MODE 4 + #define ECC_MODE_MASK GENMASK(5, 4) ++#define ECC_MODE_4BIT 0 ++#define ECC_MODE_8BIT 1 + #define ECC_PARITY_SIZE_BYTES_BCH 8 + #define ECC_PARITY_SIZE_BYTES_BCH_MASK GENMASK(12, 8) + #define ECC_NUM_DATA_BYTES 16 diff --git a/target/linux/generic/backport-6.12/417-v6.17-spi-spi-qpic-snand-add-support-for-8-bits-ECC-strength.patch b/target/linux/generic/backport-6.12/417-v6.17-spi-spi-qpic-snand-add-support-for-8-bits-ECC-strength.patch new file mode 100644 index 00000000000..4eff84f4197 --- /dev/null +++ b/target/linux/generic/backport-6.12/417-v6.17-spi-spi-qpic-snand-add-support-for-8-bits-ECC-strength.patch @@ -0,0 +1,66 @@ +From 913bf8d50cbd144c87e9660b591781179182ff59 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Wed, 2 Jul 2025 14:35:24 +0200 +Subject: spi: spi-qpic-snand: add support for 8 bits ECC strength + +Even though the hardware supports 8 bits ECC strength, but that is not +handled in the driver yet. This change adds the missing bits in order +to allow using the driver with chips which require 8 bits ECC strength. + +No functional changes intended with regard to the existing 4 bits ECC +strength support. + +Tested on an IPQ9574 platform using a GigaDevice GD5F2GM7REYIG chip. + +Signed-off-by: Gabor Juhos +Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-2-ae2c17a30bb7@gmail.com +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 21 ++++++++++++++++----- + 1 file changed, 16 insertions(+), 5 deletions(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -283,9 +283,22 @@ static int qcom_spi_ecc_init_ctx_pipelin + goto err_free_ecc_cfg; + } + +- if (ecc_cfg->strength != 4) { ++ switch (ecc_cfg->strength) { ++ case 4: ++ ecc_cfg->ecc_mode = ECC_MODE_4BIT; ++ ecc_cfg->ecc_bytes_hw = 7; ++ ecc_cfg->spare_bytes = 4; ++ break; ++ ++ case 8: ++ ecc_cfg->ecc_mode = ECC_MODE_8BIT; ++ ecc_cfg->ecc_bytes_hw = 13; ++ ecc_cfg->spare_bytes = 2; ++ break; ++ ++ default: + dev_err(snandc->dev, +- "only 4 bits ECC strength is supported\n"); ++ "only 4 or 8 bits ECC strength is supported\n"); + ret = -EOPNOTSUPP; + goto err_free_ecc_cfg; + } +@@ -302,8 +315,6 @@ static int qcom_spi_ecc_init_ctx_pipelin + nand->ecc.ctx.priv = ecc_cfg; + snandc->qspi->mtd = mtd; + +- ecc_cfg->ecc_bytes_hw = 7; +- ecc_cfg->spare_bytes = 4; + ecc_cfg->bbm_size = 1; + ecc_cfg->bch_enabled = true; + ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size; +@@ -365,7 +376,7 @@ static int qcom_spi_ecc_init_ctx_pipelin + FIELD_PREP(ECC_SW_RESET, 0) | + FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) | + FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | +- FIELD_PREP(ECC_MODE_MASK, ECC_MODE_4BIT) | ++ FIELD_PREP(ECC_MODE_MASK, ecc_cfg->ecc_mode) | + FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); + + ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS; diff --git a/target/linux/generic/backport-6.12/418-v6.17-spi-spi-qpic-snand-use-correct-CW_PER_PAGE-value-for.patch b/target/linux/generic/backport-6.12/418-v6.17-spi-spi-qpic-snand-use-correct-CW_PER_PAGE-value-for.patch new file mode 100644 index 00000000000..b67645e07cf --- /dev/null +++ b/target/linux/generic/backport-6.12/418-v6.17-spi-spi-qpic-snand-use-correct-CW_PER_PAGE-value-for.patch @@ -0,0 +1,56 @@ +From 6bc829220b33da8522572cc50fdf5067c51d3bf3 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Fri, 1 Aug 2025 09:58:35 +0200 +Subject: spi: spi-qpic-snand: use correct CW_PER_PAGE value for OOB write + +The qcom_spi_program_oob() function uses only the last codeword to write +the OOB data into the flash, but it sets the CW_PER_PAGE field in the +CFG0 register as it would use all codewords. + +It seems that this confuses the hardware somehow, and any access to the +flash fails with a timeout error after the function is called. The problem +can be easily reproduced with the following commands: + + # dd if=/dev/zero bs=2176 count=1 > /tmp/test.bin + 1+0 records in + 1+0 records out + # flash_erase /dev/mtd4 0 0 + Erasing 128 Kibyte @ 0 -- 100 % complete + # nandwrite -O /dev/mtd4 /tmp/test.bin + Writing data to block 0 at offset 0x0 + # nanddump -o /dev/mtd4 >/dev/null + ECC failed: 0 + ECC corrected: 0 + Number of bad blocks: 0 + Number of bbt blocks: 0 + Block size 131072, page size 2048, OOB size 128 + Dumping data starting at 0x00000000 and ending at 0x00020000... + [ 33.197605] qcom_snand 79b0000.spi: failure to read oob + libmtd: error!: MEMREADOOB64 ioctl failed for mtd4, offset 0 (eraseblock 0) + error 110 (Operation timed out) + [ 35.277582] qcom_snand 79b0000.spi: failure in submitting cmd descriptor + libmtd: error!: cannot read 2048 bytes from mtd4 (eraseblock 0, offset 2048) + error 110 (Operation timed out) + nanddump: error!: mtd_read + +Change the code to use the correct CW_PER_PAGE value to avoid this. + +Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Gabor Juhos +Link: https://patch.msgid.link/20250801-qpic-snand-oob-cwpp-fix-v1-1-f5a41b86af2e@gmail.com +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -1204,7 +1204,7 @@ static int qcom_spi_program_oob(struct q + u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; + + cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) | +- FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); ++ FIELD_PREP(CW_PER_PAGE_MASK, 0); + cfg1 = ecc_cfg->cfg1; + ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; + ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; diff --git a/target/linux/generic/backport-6.12/419-v6.17-spi-spi-qpic-snand-fix-calculating-of-ECC-OOB-region.patch b/target/linux/generic/backport-6.12/419-v6.17-spi-spi-qpic-snand-fix-calculating-of-ECC-OOB-region.patch new file mode 100644 index 00000000000..35d5d302323 --- /dev/null +++ b/target/linux/generic/backport-6.12/419-v6.17-spi-spi-qpic-snand-fix-calculating-of-ECC-OOB-region.patch @@ -0,0 +1,69 @@ +From 13d0fe84a214658254a7412b2b46ec1507dc51f0 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Tue, 5 Aug 2025 18:05:42 +0200 +Subject: spi: spi-qpic-snand: fix calculating of ECC OOB regions' properties + +The OOB layout used by the driver has two distinct regions which contains +hardware specific ECC data, yet the qcom_spi_ooblayout_ecc() function sets +the same offset and length values for both regions which is clearly wrong. + +Change the code to calculate the correct values for both regions. + +For reference, the following table shows the computed offset and length +values for various OOB size/ECC strength configurations: + + +-----------------+-----------------+ + |before the change| after the change| + +-------+----------+--------+--------+--------+--------+--------+ + | OOB | ECC | region | region | region | region | region | + | size | strength | index | offset | length | offset | length | + +-------+----------+--------+--------+--------+--------+--------+ + | 128 | 8 | 0 | 113 | 15 | 0 | 49 | + | | | 1 | 113 | 15 | 65 | 63 | + +-------+----------+--------+--------+--------+--------+--------+ + | 128 | 4 | 0 | 117 | 11 | 0 | 37 | + | | | 1 | 117 | 11 | 53 | 75 | + +-------+----------+--------+--------+--------+--------+--------+ + | 64 | 4 | 0 | 53 | 11 | 0 | 37 | + | | | 1 | 53 | 11 | 53 | 11 | + +-------+----------+--------+--------+--------+--------+--------+ + +Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Gabor Juhos +Reviewed-by: Konrad Dybcio +Link: https://patch.msgid.link/20250805-qpic-snand-oob-ecc-fix-v2-1-e6f811c70d6f@gmail.com +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -216,13 +216,21 @@ static int qcom_spi_ooblayout_ecc(struct + struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); + struct qpic_ecc *qecc = snandc->qspi->ecc; + +- if (section > 1) +- return -ERANGE; ++ switch (section) { ++ case 0: ++ oobregion->offset = 0; ++ oobregion->length = qecc->bytes * (qecc->steps - 1) + ++ qecc->bbm_size; ++ return 0; ++ case 1: ++ oobregion->offset = qecc->bytes * (qecc->steps - 1) + ++ qecc->bbm_size + ++ qecc->steps * 4; ++ oobregion->length = mtd->oobsize - oobregion->offset; ++ return 0; ++ } + +- oobregion->length = qecc->ecc_bytes_hw + qecc->spare_bytes; +- oobregion->offset = mtd->oobsize - oobregion->length; +- +- return 0; ++ return -ERANGE; + } + + static int qcom_spi_ooblayout_free(struct mtd_info *mtd, int section, diff --git a/target/linux/generic/backport-6.12/420-01-v6.16-mtd-rawnand-brcmnand-remove-unused-parameters.patch b/target/linux/generic/backport-6.12/420-01-v6.16-mtd-rawnand-brcmnand-remove-unused-parameters.patch new file mode 100644 index 00000000000..3bc6038711f --- /dev/null +++ b/target/linux/generic/backport-6.12/420-01-v6.16-mtd-rawnand-brcmnand-remove-unused-parameters.patch @@ -0,0 +1,88 @@ +From 56fce75470041b5b0d92ae10637416e1a4cceb1b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 14 May 2025 08:14:54 +0200 +Subject: [PATCH] mtd: rawnand: brcmnand: remove unused parameters +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +last_cmd and last_byte are now unused brcmnand_host members. +last_addr is only written and never read so we can remove it too. + +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Florian Fainelli +Reviewed-by: William Zhang +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/raw/brcmnand/brcmnand.c | 24 ++++++------------------ + 1 file changed, 6 insertions(+), 18 deletions(-) + +--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c ++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c +@@ -310,9 +310,6 @@ struct brcmnand_host { + struct platform_device *pdev; + int cs; + +- unsigned int last_cmd; +- unsigned int last_byte; +- u64 last_addr; + struct brcmnand_cfg hwcfg; + struct brcmnand_controller *ctrl; + }; +@@ -2233,14 +2230,11 @@ static int brcmnand_read_page(struct nan + int oob_required, int page) + { + struct mtd_info *mtd = nand_to_mtd(chip); +- struct brcmnand_host *host = nand_get_controller_data(chip); + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + u64 addr = (u64)page << chip->page_shift; + +- host->last_addr = addr; +- +- return brcmnand_read(mtd, chip, host->last_addr, +- mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); ++ return brcmnand_read(mtd, chip, addr, mtd->writesize >> FC_SHIFT, ++ (u32 *)buf, oob); + } + + static int brcmnand_read_page_raw(struct nand_chip *chip, uint8_t *buf, +@@ -2252,11 +2246,9 @@ static int brcmnand_read_page_raw(struct + int ret; + u64 addr = (u64)page << chip->page_shift; + +- host->last_addr = addr; +- + brcmnand_set_ecc_enabled(host, 0); +- ret = brcmnand_read(mtd, chip, host->last_addr, +- mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); ++ ret = brcmnand_read(mtd, chip, addr, mtd->writesize >> FC_SHIFT, ++ (u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + return ret; + } +@@ -2363,13 +2355,10 @@ static int brcmnand_write_page(struct na + int oob_required, int page) + { + struct mtd_info *mtd = nand_to_mtd(chip); +- struct brcmnand_host *host = nand_get_controller_data(chip); + void *oob = oob_required ? chip->oob_poi : NULL; + u64 addr = (u64)page << chip->page_shift; + +- host->last_addr = addr; +- +- return brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); ++ return brcmnand_write(mtd, chip, addr, (const u32 *)buf, oob); + } + + static int brcmnand_write_page_raw(struct nand_chip *chip, const uint8_t *buf, +@@ -2381,9 +2370,8 @@ static int brcmnand_write_page_raw(struc + u64 addr = (u64)page << chip->page_shift; + int ret = 0; + +- host->last_addr = addr; + brcmnand_set_ecc_enabled(host, 0); +- ret = brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); ++ ret = brcmnand_write(mtd, chip, addr, (const u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + + return ret; diff --git a/target/linux/generic/backport-6.12/420-02-v6.16-mtd-nand-brcmnand-fix-NAND-timeout-when-accessing-eM.patch b/target/linux/generic/backport-6.12/420-02-v6.16-mtd-nand-brcmnand-fix-NAND-timeout-when-accessing-eM.patch new file mode 100644 index 00000000000..d3dbde8b9e6 --- /dev/null +++ b/target/linux/generic/backport-6.12/420-02-v6.16-mtd-nand-brcmnand-fix-NAND-timeout-when-accessing-eM.patch @@ -0,0 +1,30 @@ +From 528b541b71cf03e263272b051b70696f92258e9d Mon Sep 17 00:00:00 2001 +From: David Regan +Date: Thu, 22 May 2025 10:25:17 -0700 +Subject: [PATCH] mtd: nand: brcmnand: fix NAND timeout when accessing eMMC + +When booting a board to NAND and accessing NAND while eMMC +transactions are occurring the NAND will sometimes timeout. This +is due to both NAND and eMMC controller sharing the same data bus +on BCMBCA chips. Fix is to extend NAND timeout to allow eMMC +transactions time to complete. + +Signed-off-by: David Regan +Reviewed-by: William Zhang +Reviewed-by: Florian Fainelli +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/raw/brcmnand/brcmnand.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c ++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c +@@ -101,7 +101,7 @@ struct brcm_nand_dma_desc { + #define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024) + + #define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY) +-#define NAND_POLL_STATUS_TIMEOUT_MS 100 ++#define NAND_POLL_STATUS_TIMEOUT_MS 500 + + #define EDU_CMD_WRITE 0x00 + #define EDU_CMD_READ 0x01 diff --git a/target/linux/generic/backport-6.12/420-03-v6.16-mtd-rawnand-brcmnand-legacy-exec_op-implementation.patch b/target/linux/generic/backport-6.12/420-03-v6.16-mtd-rawnand-brcmnand-legacy-exec_op-implementation.patch new file mode 100644 index 00000000000..6c75c2bbf2a --- /dev/null +++ b/target/linux/generic/backport-6.12/420-03-v6.16-mtd-rawnand-brcmnand-legacy-exec_op-implementation.patch @@ -0,0 +1,299 @@ +From 3bfb22cecfe6b6f0d8ee56ef4b533cf68599c5d9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 21 May 2025 10:03:25 +0200 +Subject: [PATCH] mtd: rawnand: brcmnand: legacy exec_op implementation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit 3c8260ce7663 ("mtd: rawnand: brcmnand: exec_op implementation") +removed legacy interface functions, breaking < v5.0 controllers support. +In order to fix older controllers we need to add an alternative exec_op +implementation which doesn't rely on low level registers. + +Fixes: 3c8260ce7663 ("mtd: rawnand: brcmnand: exec_op implementation") +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: David Regan +Reviewed-by: Florian Fainelli +Reviewed-by: William Zhang +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/raw/brcmnand/brcmnand.c | 222 ++++++++++++++++++++++- + 1 file changed, 215 insertions(+), 7 deletions(-) + +--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c ++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c +@@ -65,6 +65,7 @@ module_param(wp_on, int, 0444); + #define CMD_PARAMETER_READ 0x0e + #define CMD_PARAMETER_CHANGE_COL 0x0f + #define CMD_LOW_LEVEL_OP 0x10 ++#define CMD_NOT_SUPPORTED 0xff + + struct brcm_nand_dma_desc { + u32 next_desc; +@@ -199,6 +200,30 @@ static const u16 flash_dma_regs_v4[] = { + [FLASH_DMA_CURRENT_DESC_EXT] = 0x34, + }; + ++/* Native command conversion for legacy controllers (< v5.0) */ ++static const u8 native_cmd_conv[] = { ++ [NAND_CMD_READ0] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_READ1] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_RNDOUT] = CMD_PARAMETER_CHANGE_COL, ++ [NAND_CMD_PAGEPROG] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_READOOB] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_ERASE1] = CMD_BLOCK_ERASE, ++ [NAND_CMD_STATUS] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_SEQIN] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_RNDIN] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_READID] = CMD_DEVICE_ID_READ, ++ [NAND_CMD_ERASE2] = CMD_NULL, ++ [NAND_CMD_PARAM] = CMD_PARAMETER_READ, ++ [NAND_CMD_GET_FEATURES] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_SET_FEATURES] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_RESET] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_READSTART] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_READCACHESEQ] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_READCACHEEND] = CMD_NOT_SUPPORTED, ++ [NAND_CMD_RNDOUTSTART] = CMD_NULL, ++ [NAND_CMD_CACHEDPROG] = CMD_NOT_SUPPORTED, ++}; ++ + /* Controller feature flags */ + enum { + BRCMNAND_HAS_1K_SECTORS = BIT(0), +@@ -237,6 +262,12 @@ struct brcmnand_controller { + /* List of NAND hosts (one for each chip-select) */ + struct list_head host_list; + ++ /* Functions to be called from exec_op */ ++ int (*check_instr)(struct nand_chip *chip, ++ const struct nand_operation *op); ++ int (*exec_instr)(struct nand_chip *chip, ++ const struct nand_operation *op); ++ + /* EDU info, per-transaction */ + const u16 *edu_offsets; + void __iomem *edu_base; +@@ -2478,18 +2509,190 @@ static int brcmnand_op_is_reset(const st + return 0; + } + ++static int brcmnand_check_instructions(struct nand_chip *chip, ++ const struct nand_operation *op) ++{ ++ return 0; ++} ++ ++static int brcmnand_exec_instructions(struct nand_chip *chip, ++ const struct nand_operation *op) ++{ ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ unsigned int i; ++ int ret = 0; ++ ++ for (i = 0; i < op->ninstrs; i++) { ++ ret = brcmnand_exec_instr(host, i, op); ++ if (ret) ++ break; ++ } ++ ++ return ret; ++} ++ ++static int brcmnand_check_instructions_legacy(struct nand_chip *chip, ++ const struct nand_operation *op) ++{ ++ const struct nand_op_instr *instr; ++ unsigned int i; ++ u8 cmd; ++ ++ for (i = 0; i < op->ninstrs; i++) { ++ instr = &op->instrs[i]; ++ ++ switch (instr->type) { ++ case NAND_OP_CMD_INSTR: ++ cmd = native_cmd_conv[instr->ctx.cmd.opcode]; ++ if (cmd == CMD_NOT_SUPPORTED) ++ return -EOPNOTSUPP; ++ break; ++ case NAND_OP_ADDR_INSTR: ++ case NAND_OP_DATA_IN_INSTR: ++ case NAND_OP_WAITRDY_INSTR: ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ return 0; ++} ++ ++static int brcmnand_exec_instructions_legacy(struct nand_chip *chip, ++ const struct nand_operation *op) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_controller *ctrl = host->ctrl; ++ const struct nand_op_instr *instr; ++ unsigned int i, j; ++ u8 cmd = CMD_NULL, last_cmd = CMD_NULL; ++ int ret = 0; ++ u64 last_addr; ++ ++ for (i = 0; i < op->ninstrs; i++) { ++ instr = &op->instrs[i]; ++ ++ if (instr->type == NAND_OP_CMD_INSTR) { ++ cmd = native_cmd_conv[instr->ctx.cmd.opcode]; ++ if (cmd == CMD_NOT_SUPPORTED) { ++ dev_err(ctrl->dev, "unsupported cmd=%d\n", ++ instr->ctx.cmd.opcode); ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ } else if (instr->type == NAND_OP_ADDR_INSTR) { ++ u64 addr = 0; ++ ++ if (cmd == CMD_NULL) ++ continue; ++ ++ if (instr->ctx.addr.naddrs > 8) { ++ dev_err(ctrl->dev, "unsupported naddrs=%u\n", ++ instr->ctx.addr.naddrs); ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ for (j = 0; j < instr->ctx.addr.naddrs; j++) ++ addr |= (instr->ctx.addr.addrs[j]) << (j << 3); ++ ++ if (cmd == CMD_BLOCK_ERASE) ++ addr <<= chip->page_shift; ++ else if (cmd == CMD_PARAMETER_CHANGE_COL) ++ addr &= ~((u64)(FC_BYTES - 1)); ++ ++ brcmnand_set_cmd_addr(mtd, addr); ++ brcmnand_send_cmd(host, cmd); ++ last_addr = addr; ++ last_cmd = cmd; ++ cmd = CMD_NULL; ++ brcmnand_waitfunc(chip); ++ ++ if (last_cmd == CMD_PARAMETER_READ || ++ last_cmd == CMD_PARAMETER_CHANGE_COL) { ++ /* Copy flash cache word-wise */ ++ u32 *flash_cache = (u32 *)ctrl->flash_cache; ++ ++ brcmnand_soc_data_bus_prepare(ctrl->soc, true); ++ ++ /* ++ * Must cache the FLASH_CACHE now, since changes in ++ * SECTOR_SIZE_1K may invalidate it ++ */ ++ for (j = 0; j < FC_WORDS; j++) ++ /* ++ * Flash cache is big endian for parameter pages, at ++ * least on STB SoCs ++ */ ++ flash_cache[j] = be32_to_cpu(brcmnand_read_fc(ctrl, j)); ++ ++ brcmnand_soc_data_bus_unprepare(ctrl->soc, true); ++ } ++ } else if (instr->type == NAND_OP_DATA_IN_INSTR) { ++ u8 *in = instr->ctx.data.buf.in; ++ ++ if (last_cmd == CMD_DEVICE_ID_READ) { ++ u32 val; ++ ++ if (instr->ctx.data.len > 8) { ++ dev_err(ctrl->dev, "unsupported len=%u\n", ++ instr->ctx.data.len); ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ for (j = 0; j < instr->ctx.data.len; j++) { ++ if (j == 0) ++ val = brcmnand_read_reg(ctrl, BRCMNAND_ID); ++ else if (j == 4) ++ val = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT); ++ ++ in[j] = (val >> (24 - ((j % 4) << 3))) & 0xff; ++ } ++ } else if (last_cmd == CMD_PARAMETER_READ || ++ last_cmd == CMD_PARAMETER_CHANGE_COL) { ++ u64 addr; ++ u32 offs; ++ ++ for (j = 0; j < instr->ctx.data.len; j++) { ++ addr = last_addr + j; ++ offs = addr & (FC_BYTES - 1); ++ ++ if (j > 0 && offs == 0) ++ nand_change_read_column_op(chip, addr, NULL, 0, ++ false); ++ ++ in[j] = ctrl->flash_cache[offs]; ++ } ++ } ++ } else if (instr->type == NAND_OP_WAITRDY_INSTR) { ++ ret = bcmnand_ctrl_poll_status(host, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); ++ if (ret) ++ break; ++ } else { ++ dev_err(ctrl->dev, "unsupported instruction type: %d\n", instr->type); ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ } ++ ++ return ret; ++} ++ + static int brcmnand_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) + { + struct brcmnand_host *host = nand_get_controller_data(chip); ++ struct brcmnand_controller *ctrl = host->ctrl; + struct mtd_info *mtd = nand_to_mtd(chip); + u8 *status; +- unsigned int i; + int ret = 0; + + if (check_only) +- return 0; ++ return ctrl->check_instr(chip, op); + + if (brcmnand_op_is_status(op)) { + status = op->instrs[1].ctx.data.buf.in; +@@ -2513,11 +2716,7 @@ static int brcmnand_exec_op(struct nand_ + if (op->deassert_wp) + brcmnand_wp(mtd, 0); + +- for (i = 0; i < op->ninstrs; i++) { +- ret = brcmnand_exec_instr(host, i, op); +- if (ret) +- break; +- } ++ ret = ctrl->exec_instr(chip, op); + + if (op->deassert_wp) + brcmnand_wp(mtd, 1); +@@ -3130,6 +3329,15 @@ int brcmnand_probe(struct platform_devic + if (ret) + goto err; + ++ /* Only v5.0+ controllers have low level ops support */ ++ if (ctrl->nand_version >= 0x0500) { ++ ctrl->check_instr = brcmnand_check_instructions; ++ ctrl->exec_instr = brcmnand_exec_instructions; ++ } else { ++ ctrl->check_instr = brcmnand_check_instructions_legacy; ++ ctrl->exec_instr = brcmnand_exec_instructions_legacy; ++ } ++ + /* + * Most chips have this cache at a fixed offset within 'nand' block. + * Some must specify this region separately. diff --git a/target/linux/generic/backport-6.12/422-v6.17-spi-spi-qpic-snand-unregister-ECC-engine-on-probe-er.patch b/target/linux/generic/backport-6.12/422-v6.17-spi-spi-qpic-snand-unregister-ECC-engine-on-probe-er.patch new file mode 100644 index 00000000000..46184b53c5c --- /dev/null +++ b/target/linux/generic/backport-6.12/422-v6.17-spi-spi-qpic-snand-unregister-ECC-engine-on-probe-er.patch @@ -0,0 +1,48 @@ +From 1991a458528588ff34e98b6365362560d208710f Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Wed, 3 Sep 2025 13:56:24 +0200 +Subject: spi: spi-qpic-snand: unregister ECC engine on probe error and device + remove + +The on-host hardware ECC engine remains registered both when +the spi_register_controller() function returns with an error +and also on device removal. + +Change the qcom_spi_probe() function to unregister the engine +on the error path, and add the missing unregistering call to +qcom_spi_remove() to avoid possible use-after-free issues. + +Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") +Signed-off-by: Gabor Juhos +Message-ID: <20250903-qpic-snand-unregister-ecceng-v1-1-ef5387b0abdc@gmail.com> +Signed-off-by: Mark Brown +--- + drivers/spi/spi-qpic-snand.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -1632,11 +1632,13 @@ static int qcom_spi_probe(struct platfor + ret = spi_register_controller(ctlr); + if (ret) { + dev_err(&pdev->dev, "spi_register_controller failed.\n"); +- goto err_spi_init; ++ goto err_register_controller; + } + + return 0; + ++err_register_controller: ++ nand_ecc_unregister_on_host_hw_engine(&snandc->qspi->ecc_eng); + err_spi_init: + qcom_nandc_unalloc(snandc); + err_snand_alloc: +@@ -1658,7 +1660,7 @@ static void qcom_spi_remove(struct platf + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + spi_unregister_controller(ctlr); +- ++ nand_ecc_unregister_on_host_hw_engine(&snandc->qspi->ecc_eng); + qcom_nandc_unalloc(snandc); + + clk_disable_unprepare(snandc->aon_clk); diff --git a/target/linux/generic/backport-6.12/429-01-v6.18-mtd-spinand-fix-direct-mapping-creation-sizes.patch b/target/linux/generic/backport-6.12/429-01-v6.18-mtd-spinand-fix-direct-mapping-creation-sizes.patch new file mode 100644 index 00000000000..6abc202337a --- /dev/null +++ b/target/linux/generic/backport-6.12/429-01-v6.18-mtd-spinand-fix-direct-mapping-creation-sizes.patch @@ -0,0 +1,63 @@ +From e4a0cf9f1d90e6888e5373da3314f761024f6c97 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Thu, 18 Sep 2025 00:53:59 +0300 +Subject: mtd: spinand: fix direct mapping creation sizes + +Continuous mode is only supported for data reads, thus writing +requires only single flash page mapping. + +Signed-off-by: Mikhail Kshevetskiy +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/spi/core.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -1036,18 +1036,13 @@ static int spinand_create_dirmap(struct + unsigned int plane) + { + struct nand_device *nand = spinand_to_nand(spinand); +- struct spi_mem_dirmap_info info = { +- .length = nanddev_page_size(nand) + +- nanddev_per_page_oobsize(nand), +- }; ++ struct spi_mem_dirmap_info info = { 0 }; + struct spi_mem_dirmap_desc *desc; + +- if (spinand->cont_read_possible) +- info.length = nanddev_eraseblock_size(nand); +- + /* The plane number is passed in MSB just above the column address */ + info.offset = plane << fls(nand->memorg.pagesize); + ++ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); + info.op_tmpl = *spinand->op_templates.update_cache; + desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, + spinand->spimem, &info); +@@ -1056,6 +1051,8 @@ static int spinand_create_dirmap(struct + + spinand->dirmaps[plane].wdesc = desc; + ++ if (spinand->cont_read_possible) ++ info.length = nanddev_eraseblock_size(nand); + info.op_tmpl = *spinand->op_templates.read_cache; + desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, + spinand->spimem, &info); +@@ -1071,6 +1068,7 @@ static int spinand_create_dirmap(struct + return 0; + } + ++ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); + info.op_tmpl = *spinand->op_templates.update_cache; + info.op_tmpl.data.ecc = true; + desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, +@@ -1080,6 +1078,8 @@ static int spinand_create_dirmap(struct + + spinand->dirmaps[plane].wdesc_ecc = desc; + ++ if (spinand->cont_read_possible) ++ info.length = nanddev_eraseblock_size(nand); + info.op_tmpl = *spinand->op_templates.read_cache; + info.op_tmpl.data.ecc = true; + desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, diff --git a/target/linux/generic/backport-6.12/429-02-v6.18-mtd-spinand-try-a-regular-dirmap-if-creating-a-di.patch b/target/linux/generic/backport-6.12/429-02-v6.18-mtd-spinand-try-a-regular-dirmap-if-creating-a-di.patch new file mode 100644 index 00000000000..cc94e6d9577 --- /dev/null +++ b/target/linux/generic/backport-6.12/429-02-v6.18-mtd-spinand-try-a-regular-dirmap-if-creating-a-di.patch @@ -0,0 +1,98 @@ +From 004f8ea0d9917398aabff7388b3bf62a84a4088b Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Thu, 18 Sep 2025 00:54:00 +0300 +Subject: mtd: spinand: try a regular dirmap if creating a dirmap for + continuous reading fails + +Continuous reading may result in multiple flash pages reading in one +operation. Typically only one flash page has read/written (a little bit +more than 2-4 Kb), but continuous reading requires the spi controller +to read up to 512 Kb in one operation without toggling CS in beetween. + +Roughly speaking spi controllers can be divided on 2 categories: + * spi controllers without dirmap acceleration support + * spi controllers with dirmap acceleration support + +Firt of them will have issues with continuous reading if restriction on +the transfer length is implemented in the adjust_op_size() handler. +Second group often supports acceleration of single page only reading. +Thus enabling of continuous reading can break flash reading. + +This patch tries to create dirmap for continuous reading first and +fallback to regular reading if spi controller refuses to create it. + +Signed-off-by: Mikhail Kshevetskiy +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/spi/core.c | 43 ++++++++++++++++++++++++++++++------- + 1 file changed, 35 insertions(+), 8 deletions(-) + +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -1032,6 +1032,39 @@ static int spinand_mtd_block_isreserved( + return ret; + } + ++static struct spi_mem_dirmap_desc *spinand_create_rdesc( ++ struct spinand_device *spinand, ++ struct spi_mem_dirmap_info *info) ++{ ++ struct nand_device *nand = spinand_to_nand(spinand); ++ struct spi_mem_dirmap_desc *desc = NULL; ++ ++ if (spinand->cont_read_possible) { ++ /* ++ * spi controller may return an error if info->length is ++ * too large ++ */ ++ info->length = nanddev_eraseblock_size(nand); ++ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, ++ spinand->spimem, info); ++ } ++ ++ if (IS_ERR_OR_NULL(desc)) { ++ /* ++ * continuous reading is not supported by flash or ++ * its spi controller, use regular reading ++ */ ++ spinand->cont_read_possible = false; ++ ++ info->length = nanddev_page_size(nand) + ++ nanddev_per_page_oobsize(nand); ++ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, ++ spinand->spimem, info); ++ } ++ ++ return desc; ++} ++ + static int spinand_create_dirmap(struct spinand_device *spinand, + unsigned int plane) + { +@@ -1051,11 +1084,8 @@ static int spinand_create_dirmap(struct + + spinand->dirmaps[plane].wdesc = desc; + +- if (spinand->cont_read_possible) +- info.length = nanddev_eraseblock_size(nand); + info.op_tmpl = *spinand->op_templates.read_cache; +- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, +- spinand->spimem, &info); ++ desc = spinand_create_rdesc(spinand, &info); + if (IS_ERR(desc)) + return PTR_ERR(desc); + +@@ -1078,12 +1108,9 @@ static int spinand_create_dirmap(struct + + spinand->dirmaps[plane].wdesc_ecc = desc; + +- if (spinand->cont_read_possible) +- info.length = nanddev_eraseblock_size(nand); + info.op_tmpl = *spinand->op_templates.read_cache; + info.op_tmpl.data.ecc = true; +- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, +- spinand->spimem, &info); ++ desc = spinand_create_rdesc(spinand, &info); + if (IS_ERR(desc)) + return PTR_ERR(desc); + diff --git a/target/linux/generic/backport-6.12/429-03-v6.18-mtd-spinand-repeat-reading-in-regular-mode-if-con.patch b/target/linux/generic/backport-6.12/429-03-v6.18-mtd-spinand-repeat-reading-in-regular-mode-if-con.patch new file mode 100644 index 00000000000..38d57122494 --- /dev/null +++ b/target/linux/generic/backport-6.12/429-03-v6.18-mtd-spinand-repeat-reading-in-regular-mode-if-con.patch @@ -0,0 +1,64 @@ +From 010dc7f2dd6a0078ade3f88f627ed5fbf45ceb94 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Thu, 18 Sep 2025 00:54:01 +0300 +Subject: mtd: spinand: repeat reading in regular mode if continuous reading + fails + +Continuous reading may result in multiple flash pages reading in one +operation. Unfortunately, not all spinand controllers support such +large reading. They will read less data. Unfortunately, the operation +can't be continued. + +In this case: + * disable continuous reading on this (not good enough) spi controller + * repeat reading in regular mode. + +Signed-off-by: Mikhail Kshevetskiy +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/spi/core.c | 25 +++++++++++++++++++++---- + 1 file changed, 21 insertions(+), 4 deletions(-) + +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -427,8 +427,16 @@ static int spinand_read_from_cache_op(st + * Dirmap accesses are allowed to toggle the CS. + * Toggling the CS during a continuous read is forbidden. + */ +- if (nbytes && req->continuous) +- return -EIO; ++ if (nbytes && req->continuous) { ++ /* ++ * Spi controller with broken support of continuous ++ * reading was detected. Disable future use of ++ * continuous reading and return -EAGAIN to retry ++ * reading within regular mode. ++ */ ++ spinand->cont_read_possible = false; ++ return -EAGAIN; ++ } + } + + if (req->datalen) +@@ -849,10 +857,19 @@ static int spinand_mtd_read(struct mtd_i + + old_stats = mtd->ecc_stats; + +- if (spinand_use_cont_read(mtd, from, ops)) ++ if (spinand_use_cont_read(mtd, from, ops)) { + ret = spinand_mtd_continuous_page_read(mtd, from, ops, &max_bitflips); +- else ++ if (ret == -EAGAIN && !spinand->cont_read_possible) { ++ /* ++ * Spi controller with broken support of continuous ++ * reading was detected (see spinand_read_from_cache_op()), ++ * repeat reading in regular mode. ++ */ ++ ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips); ++ } ++ } else { + ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips); ++ } + + if (ops->stats) { + ops->stats->uncorrectable_errors += diff --git a/target/linux/generic/backport-6.12/430-v6.14-mtd-spinand-Introduce-a-way-to-avoid-raw-access.patch b/target/linux/generic/backport-6.12/430-v6.14-mtd-spinand-Introduce-a-way-to-avoid-raw-access.patch new file mode 100644 index 00000000000..094d89b9f45 --- /dev/null +++ b/target/linux/generic/backport-6.12/430-v6.14-mtd-spinand-Introduce-a-way-to-avoid-raw-access.patch @@ -0,0 +1,81 @@ +From 6d9d6ab3a82af50e36e13e7bc8e2d1b970e39f79 Mon Sep 17 00:00:00 2001 +From: Takahiro Kuwano +Date: Tue, 3 Dec 2024 11:46:49 +0900 +Subject: [PATCH 1/1] mtd: spinand: Introduce a way to avoid raw access + +SkyHigh spinand device has ECC enable bit in configuration register but +it must be always enabled. If ECC is disabled, read and write ops +results in undetermined state. For such devices, a way to avoid raw +access is needed. + +Introduce SPINAND_NO_RAW_ACCESS flag to advertise the device does not +support raw access. In such devices, the on-die ECC engine ops returns +error to I/O request in raw mode. + +Checking and marking BBM need to be cared as special case, by adding +fallback mechanism that tries read/write OOB with ECC enabled. + +Signed-off-by: Takahiro Kuwano +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/spi/core.c | 22 ++++++++++++++++++++-- + include/linux/mtd/spinand.h | 1 + + 2 files changed, 21 insertions(+), 2 deletions(-) + +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -294,6 +294,9 @@ static int spinand_ondie_ecc_prepare_io_ + struct spinand_device *spinand = nand_to_spinand(nand); + bool enable = (req->mode != MTD_OPS_RAW); + ++ if (!enable && spinand->flags & SPINAND_NO_RAW_ACCESS) ++ return -EOPNOTSUPP; ++ + memset(spinand->oobbuf, 0xff, nanddev_per_page_oobsize(nand)); + + /* Only enable or disable the engine */ +@@ -929,9 +932,17 @@ static bool spinand_isbad(struct nand_de + .oobbuf.in = marker, + .mode = MTD_OPS_RAW, + }; ++ int ret; + + spinand_select_target(spinand, pos->target); +- spinand_read_page(spinand, &req); ++ ++ ret = spinand_read_page(spinand, &req); ++ if (ret == -EOPNOTSUPP) { ++ /* Retry with ECC in case raw access is not supported */ ++ req.mode = MTD_OPS_PLACE_OOB; ++ spinand_read_page(spinand, &req); ++ } ++ + if (marker[0] != 0xff || marker[1] != 0xff) + return true; + +@@ -974,7 +985,14 @@ static int spinand_markbad(struct nand_d + if (ret) + return ret; + +- return spinand_write_page(spinand, &req); ++ ret = spinand_write_page(spinand, &req); ++ if (ret == -EOPNOTSUPP) { ++ /* Retry with ECC in case raw access is not supported */ ++ req.mode = MTD_OPS_PLACE_OOB; ++ ret = spinand_write_page(spinand, &req); ++ } ++ ++ return ret; + } + + static int spinand_mtd_block_markbad(struct mtd_info *mtd, loff_t offs) +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -315,6 +315,7 @@ struct spinand_ecc_info { + #define SPINAND_HAS_CR_FEAT_BIT BIT(1) + #define SPINAND_HAS_PROG_PLANE_SELECT_BIT BIT(2) + #define SPINAND_HAS_READ_PLANE_SELECT_BIT BIT(3) ++#define SPINAND_NO_RAW_ACCESS BIT(4) + + /** + * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure diff --git a/target/linux/generic/backport-6.12/431-v6.14-mtd-spinand-Add-support-for-SkyHigh-S35ML-3-family.patch b/target/linux/generic/backport-6.12/431-v6.14-mtd-spinand-Add-support-for-SkyHigh-S35ML-3-family.patch new file mode 100644 index 00000000000..762978d47d5 --- /dev/null +++ b/target/linux/generic/backport-6.12/431-v6.14-mtd-spinand-Add-support-for-SkyHigh-S35ML-3-family.patch @@ -0,0 +1,201 @@ +From 1a50e3612de9187857f55ee14a573f7f8e7d4ebc Mon Sep 17 00:00:00 2001 +From: Takahiro Kuwano +Date: Tue, 3 Dec 2024 11:46:50 +0900 +Subject: [PATCH] mtd: spinand: Add support for SkyHigh S35ML-3 family + +SkyHigh S35ML01G300, S35ML01G301, S35ML02G300, and S35ML04G300 are 1Gb, +2Gb, and 4Gb SLC SPI NAND flash family. This family of devices has +on-die ECC which parity bits are stored to hidden area. In this family +the on-die ECC cannot be disabled so raw access needs to be prevented. + +Link: https://www.skyhighmemory.com/download/SPI_S35ML01_04G3_002_19205.pdf?v=P +Co-developed-by: KR Kim +Signed-off-by: KR Kim +Signed-off-by: Takahiro Kuwano +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/spi/Makefile | 2 +- + drivers/mtd/nand/spi/core.c | 1 + + drivers/mtd/nand/spi/skyhigh.c | 147 +++++++++++++++++++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 4 files changed, 150 insertions(+), 1 deletion(-) + create mode 100644 drivers/mtd/nand/spi/skyhigh.c + +--- a/drivers/mtd/nand/spi/Makefile ++++ b/drivers/mtd/nand/spi/Makefile +@@ -1,4 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0 + spinand-objs := core.o alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o macronix.o +-spinand-objs += micron.o paragon.o toshiba.o winbond.o xtx.o ++spinand-objs += micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o + obj-$(CONFIG_MTD_SPI_NAND) += spinand.o +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -1191,6 +1191,7 @@ static const struct spinand_manufacturer + ¯onix_spinand_manufacturer, + µn_spinand_manufacturer, + ¶gon_spinand_manufacturer, ++ &skyhigh_spinand_manufacturer, + &toshiba_spinand_manufacturer, + &winbond_spinand_manufacturer, + &xtx_spinand_manufacturer, +--- /dev/null ++++ b/drivers/mtd/nand/spi/skyhigh.c +@@ -0,0 +1,147 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2024 SkyHigh Memory Limited ++ * ++ * Author: Takahiro Kuwano ++ * Co-Author: KR Kim ++ */ ++ ++#include ++#include ++#include ++ ++#define SPINAND_MFR_SKYHIGH 0x01 ++#define SKYHIGH_STATUS_ECC_1TO2_BITFLIPS (1 << 4) ++#define SKYHIGH_STATUS_ECC_3TO6_BITFLIPS (2 << 4) ++#define SKYHIGH_STATUS_ECC_UNCOR_ERROR (3 << 4) ++#define SKYHIGH_CONFIG_PROTECT_EN BIT(1) ++ ++static SPINAND_OP_VARIANTS(read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(write_cache_variants, ++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_variants, ++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ ++static int skyhigh_spinand_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ /* ECC bytes are stored in hidden area. */ ++ return -ERANGE; ++} ++ ++static int skyhigh_spinand_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section) ++ return -ERANGE; ++ ++ /* ECC bytes are stored in hidden area. Reserve 2 bytes for the BBM. */ ++ region->offset = 2; ++ region->length = mtd->oobsize - 2; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops skyhigh_spinand_ooblayout = { ++ .ecc = skyhigh_spinand_ooblayout_ecc, ++ .free = skyhigh_spinand_ooblayout_free, ++}; ++ ++static int skyhigh_spinand_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & STATUS_ECC_MASK) { ++ case STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case SKYHIGH_STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ case SKYHIGH_STATUS_ECC_1TO2_BITFLIPS: ++ return 2; ++ ++ case SKYHIGH_STATUS_ECC_3TO6_BITFLIPS: ++ return 6; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct spinand_info skyhigh_spinand_table[] = { ++ SPINAND_INFO("S35ML01G301", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(6, 32), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_NO_RAW_ACCESS, ++ SPINAND_ECCINFO(&skyhigh_spinand_ooblayout, ++ skyhigh_spinand_ecc_get_status)), ++ SPINAND_INFO("S35ML01G300", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(6, 32), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_NO_RAW_ACCESS, ++ SPINAND_ECCINFO(&skyhigh_spinand_ooblayout, ++ skyhigh_spinand_ecc_get_status)), ++ SPINAND_INFO("S35ML02G300", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), ++ NAND_ECCREQ(6, 32), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_NO_RAW_ACCESS, ++ SPINAND_ECCINFO(&skyhigh_spinand_ooblayout, ++ skyhigh_spinand_ecc_get_status)), ++ SPINAND_INFO("S35ML04G300", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), ++ NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 2, 1, 1), ++ NAND_ECCREQ(6, 32), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_NO_RAW_ACCESS, ++ SPINAND_ECCINFO(&skyhigh_spinand_ooblayout, ++ skyhigh_spinand_ecc_get_status)), ++}; ++ ++static int skyhigh_spinand_init(struct spinand_device *spinand) ++{ ++ /* ++ * Config_Protect_En (bit 1 in Block Lock register) must be set to 1 ++ * before writing other bits. Do it here before core unlocks all blocks ++ * by writing block protection bits. ++ */ ++ return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, ++ SKYHIGH_CONFIG_PROTECT_EN); ++} ++ ++static const struct spinand_manufacturer_ops skyhigh_spinand_manuf_ops = { ++ .init = skyhigh_spinand_init, ++}; ++ ++const struct spinand_manufacturer skyhigh_spinand_manufacturer = { ++ .id = SPINAND_MFR_SKYHIGH, ++ .name = "SkyHigh", ++ .chips = skyhigh_spinand_table, ++ .nchips = ARRAY_SIZE(skyhigh_spinand_table), ++ .ops = &skyhigh_spinand_manuf_ops, ++}; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -269,6 +269,7 @@ extern const struct spinand_manufacturer + extern const struct spinand_manufacturer macronix_spinand_manufacturer; + extern const struct spinand_manufacturer micron_spinand_manufacturer; + extern const struct spinand_manufacturer paragon_spinand_manufacturer; ++extern const struct spinand_manufacturer skyhigh_spinand_manufacturer; + extern const struct spinand_manufacturer toshiba_spinand_manufacturer; + extern const struct spinand_manufacturer winbond_spinand_manufacturer; + extern const struct spinand_manufacturer xtx_spinand_manufacturer; diff --git a/target/linux/generic/backport-6.12/434-v6.19-mtd-spinand-esmt-add-support-for-F50L1G41LC.patch b/target/linux/generic/backport-6.12/434-v6.19-mtd-spinand-esmt-add-support-for-F50L1G41LC.patch new file mode 100644 index 00000000000..ebd9eb5c183 --- /dev/null +++ b/target/linux/generic/backport-6.12/434-v6.19-mtd-spinand-esmt-add-support-for-F50L1G41LC.patch @@ -0,0 +1,84 @@ +From b98994cb9bc24f5c7575c86650f96c384576fdfa Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 17 Nov 2025 02:54:19 +0000 +Subject: [PATCH] mtd: spinand: esmt: add support for F50L1G41LC + +This adds support for ESMT F50L1G41LC, which appears to be an updated +version of the already supported F50L1G41LB. +Add esmt_8c SPI_NAND manufacturer to account for the newly used vendor +ID with support for the ESMT F50L1G41LC chip. + +Link: https://github.com/openwrt/openwrt/pull/15214#issuecomment-3514824435 +Signed-off-by: Daniel Golle +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/spi/core.c | 1 + + drivers/mtd/nand/spi/esmt.c | 24 ++++++++++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 3 files changed, 26 insertions(+) + +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -1184,6 +1184,7 @@ static const struct nand_ops spinand_ops + static const struct spinand_manufacturer *spinand_manufacturers[] = { + &alliancememory_spinand_manufacturer, + &ato_spinand_manufacturer, ++ &esmt_8c_spinand_manufacturer, + &esmt_c8_spinand_manufacturer, + &fmsh_spinand_manufacturer, + &foresee_spinand_manufacturer, +--- a/drivers/mtd/nand/spi/esmt.c ++++ b/drivers/mtd/nand/spi/esmt.c +@@ -11,6 +11,7 @@ + + /* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */ + #define SPINAND_MFR_ESMT_C8 0xc8 ++#define SPINAND_MFR_ESMT_8C 0x8c + + static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), +@@ -102,6 +103,19 @@ static const struct mtd_ooblayout_ops f5 + .free = f50l1g41lb_ooblayout_free, + }; + ++ ++static const struct spinand_info esmt_8c_spinand_table[] = { ++ SPINAND_INFO("F50L1G41LC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x2C), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(1, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), ++}; ++ + static const struct spinand_info esmt_c8_spinand_table[] = { + SPINAND_INFO("F50L1G41LB", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f, +@@ -138,6 +152,14 @@ static const struct spinand_info esmt_c8 + static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = { + }; + ++const struct spinand_manufacturer esmt_8c_spinand_manufacturer = { ++ .id = SPINAND_MFR_ESMT_8C, ++ .name = "ESMT", ++ .chips = esmt_8c_spinand_table, ++ .nchips = ARRAY_SIZE(esmt_8c_spinand_table), ++ .ops = &esmt_spinand_manuf_ops, ++}; ++ + const struct spinand_manufacturer esmt_c8_spinand_manufacturer = { + .id = SPINAND_MFR_ESMT_C8, + .name = "ESMT", +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -262,6 +262,7 @@ struct spinand_manufacturer { + /* SPI NAND manufacturers */ + extern const struct spinand_manufacturer alliancememory_spinand_manufacturer; + extern const struct spinand_manufacturer ato_spinand_manufacturer; ++extern const struct spinand_manufacturer esmt_8c_spinand_manufacturer; + extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; + extern const struct spinand_manufacturer fmsh_spinand_manufacturer; + extern const struct spinand_manufacturer foresee_spinand_manufacturer; diff --git a/target/linux/generic/backport-6.12/435-v6.19-mtd-spinand-add-support-for-FudanMicro-FM25S01BI3.patch b/target/linux/generic/backport-6.12/435-v6.19-mtd-spinand-add-support-for-FudanMicro-FM25S01BI3.patch new file mode 100644 index 00000000000..7355efe36bb --- /dev/null +++ b/target/linux/generic/backport-6.12/435-v6.19-mtd-spinand-add-support-for-FudanMicro-FM25S01BI3.patch @@ -0,0 +1,115 @@ +From f6dffe2a9ed1bdcee1879e2728310fb1e08602cf Mon Sep 17 00:00:00 2001 +From: Mikhail Zhilkin +Date: Thu, 27 Nov 2025 22:59:00 +0300 +Subject: mtd: spinand: add support for FudanMicro FM25S01BI3 + +Add support for FudanMicro FM25S01BI3 SPI NAND. + +Link: https://www.fmsh.com/nvm/FM25S01BI3_ds_eng.pdf + +Signed-off-by: Mikhail Zhilkin +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/spi/fmsh.c | 72 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 72 insertions(+) + +--- a/drivers/mtd/nand/spi/fmsh.c ++++ b/drivers/mtd/nand/spi/fmsh.c +@@ -9,6 +9,13 @@ + #include + #include + ++#define FM25S01BI3_STATUS_ECC_MASK (7 << 4) ++ #define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4) ++ #define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4) ++ #define FM25S01BI3_STATUS_ECC_UNCOR_ERROR (2 << 4) ++ #define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS (3 << 4) ++ #define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS (5 << 4) ++ + #define SPINAND_MFR_FMSH 0xA1 + + static SPINAND_OP_VARIANTS(read_cache_variants, +@@ -45,11 +52,66 @@ static int fm25s01a_ooblayout_free(struc + return 0; + } + ++static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & FM25S01BI3_STATUS_ECC_MASK) { ++ case FM25S01BI3_STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case FM25S01BI3_STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS: ++ return 3; ++ ++ case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS: ++ return 6; ++ ++ case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS: ++ return 8; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ ++static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section) ++ return -ERANGE; ++ ++ region->offset = 64; ++ region->length = 64; ++ ++ return 0; ++} ++ ++static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = (16 * section) + 4; ++ region->length = 12; ++ ++ return 0; ++} ++ + static const struct mtd_ooblayout_ops fm25s01a_ooblayout = { + .ecc = fm25s01a_ooblayout_ecc, + .free = fm25s01a_ooblayout_free, + }; + ++static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = { ++ .ecc = fm25s01bi3_ooblayout_ecc, ++ .free = fm25s01bi3_ooblayout_free, ++}; ++ + static const struct spinand_info fmsh_spinand_table[] = { + SPINAND_INFO("FM25S01A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), +@@ -60,6 +122,16 @@ static const struct spinand_info fmsh_sp + &update_cache_variants), + 0, + SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)), ++ SPINAND_INFO("FM25S01BI3", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&fm25s01bi3_ooblayout, ++ fm25s01bi3_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = { diff --git a/target/linux/generic/backport-6.12/500-01-v6.13-block-add-support-for-defining-read-only-partitions.patch b/target/linux/generic/backport-6.12/500-01-v6.13-block-add-support-for-defining-read-only-partitions.patch new file mode 100644 index 00000000000..cdf505f3cea --- /dev/null +++ b/target/linux/generic/backport-6.12/500-01-v6.13-block-add-support-for-defining-read-only-partitions.patch @@ -0,0 +1,53 @@ +From 03cb793b26834ddca170ba87057c8f883772dd45 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 3 Oct 2024 00:11:41 +0200 +Subject: [PATCH 1/5] block: add support for defining read-only partitions + +Add support for defining read-only partitions and complete support for +it in the cmdline partition parser as the additional "ro" after a +partition is scanned but never actually applied. + +Signed-off-by: Christian Marangi +Reviewed-by: Christoph Hellwig +Link: https://lore.kernel.org/r/20241002221306.4403-2-ansuelsmth@gmail.com +Signed-off-by: Jens Axboe +--- + block/blk.h | 1 + + block/partitions/cmdline.c | 3 +++ + block/partitions/core.c | 3 +++ + 3 files changed, 7 insertions(+) + +--- a/block/blk.h ++++ b/block/blk.h +@@ -574,6 +574,7 @@ void blk_free_ext_minor(unsigned int min + #define ADDPART_FLAG_NONE 0 + #define ADDPART_FLAG_RAID 1 + #define ADDPART_FLAG_WHOLEDISK 2 ++#define ADDPART_FLAG_READONLY 4 + int bdev_add_partition(struct gendisk *disk, int partno, sector_t start, + sector_t length); + int bdev_del_partition(struct gendisk *disk, int partno); +--- a/block/partitions/cmdline.c ++++ b/block/partitions/cmdline.c +@@ -237,6 +237,9 @@ static int add_part(int slot, struct cmd + put_partition(state, slot, subpart->from >> 9, + subpart->size >> 9); + ++ if (subpart->flags & PF_RDONLY) ++ state->parts[slot].flags |= ADDPART_FLAG_READONLY; ++ + info = &state->parts[slot].info; + + strscpy(info->volname, subpart->name, sizeof(info->volname)); +--- a/block/partitions/core.c ++++ b/block/partitions/core.c +@@ -373,6 +373,9 @@ static struct block_device *add_partitio + goto out_del; + } + ++ if (flags & ADDPART_FLAG_READONLY) ++ bdev_set_flag(bdev, BD_READ_ONLY); ++ + /* everything is up and running, commence */ + err = xa_insert(&disk->part_tbl, partno, bdev, GFP_KERNEL); + if (err) diff --git a/target/linux/generic/backport-6.12/500-02-v6.13-block-introduce-add_disk_fwnode.patch b/target/linux/generic/backport-6.12/500-02-v6.13-block-introduce-add_disk_fwnode.patch new file mode 100644 index 00000000000..381838d8ea0 --- /dev/null +++ b/target/linux/generic/backport-6.12/500-02-v6.13-block-introduce-add_disk_fwnode.patch @@ -0,0 +1,94 @@ +From e5f587242b6072ffab4f4a084a459a59f3035873 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 3 Oct 2024 00:11:43 +0200 +Subject: [PATCH 3/5] block: introduce add_disk_fwnode() + +Introduce add_disk_fwnode() as a replacement of device_add_disk() that +permits to pass and attach a fwnode to disk dev. + +This variant can be useful for eMMC that might have the partition table +for the disk defined in DT. A parser can later make use of the attached +fwnode to parse the related table and init the hardcoded partition for +the disk. + +device_add_disk() is converted to a simple wrapper of add_disk_fwnode() +with the fwnode entry set as NULL. + +Signed-off-by: Christian Marangi +Reviewed-by: Christoph Hellwig +Link: https://lore.kernel.org/r/20241002221306.4403-4-ansuelsmth@gmail.com +Signed-off-by: Jens Axboe +--- + block/genhd.c | 28 ++++++++++++++++++++++++---- + include/linux/blkdev.h | 3 +++ + 2 files changed, 27 insertions(+), 4 deletions(-) + +--- a/block/genhd.c ++++ b/block/genhd.c +@@ -383,16 +383,18 @@ int disk_scan_partitions(struct gendisk + } + + /** +- * device_add_disk - add disk information to kernel list ++ * add_disk_fwnode - add disk information to kernel list with fwnode + * @parent: parent device for the disk + * @disk: per-device partitioning information + * @groups: Additional per-device sysfs groups ++ * @fwnode: attached disk fwnode + * + * This function registers the partitioning information in @disk +- * with the kernel. ++ * with the kernel. Also attach a fwnode to the disk device. + */ +-int __must_check device_add_disk(struct device *parent, struct gendisk *disk, +- const struct attribute_group **groups) ++int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk, ++ const struct attribute_group **groups, ++ struct fwnode_handle *fwnode) + + { + struct device *ddev = disk_to_dev(disk); +@@ -452,6 +454,8 @@ int __must_check device_add_disk(struct + ddev->parent = parent; + ddev->groups = groups; + dev_set_name(ddev, "%s", disk->disk_name); ++ if (fwnode) ++ device_set_node(ddev, fwnode); + if (!(disk->flags & GENHD_FL_HIDDEN)) + ddev->devt = MKDEV(disk->major, disk->first_minor); + ret = device_add(ddev); +@@ -553,6 +557,22 @@ out_exit_elevator: + elevator_exit(disk->queue); + return ret; + } ++EXPORT_SYMBOL_GPL(add_disk_fwnode); ++ ++/** ++ * device_add_disk - add disk information to kernel list ++ * @parent: parent device for the disk ++ * @disk: per-device partitioning information ++ * @groups: Additional per-device sysfs groups ++ * ++ * This function registers the partitioning information in @disk ++ * with the kernel. ++ */ ++int __must_check device_add_disk(struct device *parent, struct gendisk *disk, ++ const struct attribute_group **groups) ++{ ++ return add_disk_fwnode(parent, disk, groups, NULL); ++} + EXPORT_SYMBOL(device_add_disk); + + static void blk_report_disk_dead(struct gendisk *disk, bool surprise) +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -789,6 +789,9 @@ static inline unsigned int blk_queue_dep + #define for_each_bio(_bio) \ + for (; _bio; _bio = _bio->bi_next) + ++int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk, ++ const struct attribute_group **groups, ++ struct fwnode_handle *fwnode); + int __must_check device_add_disk(struct device *parent, struct gendisk *disk, + const struct attribute_group **groups); + static inline int __must_check add_disk(struct gendisk *disk) diff --git a/target/linux/generic/backport-6.12/500-03-v6.13-mmc-block-attach-partitions-fwnode-if-found-in-mmc-c.patch b/target/linux/generic/backport-6.12/500-03-v6.13-mmc-block-attach-partitions-fwnode-if-found-in-mmc-c.patch new file mode 100644 index 00000000000..0bdeaa85e4c --- /dev/null +++ b/target/linux/generic/backport-6.12/500-03-v6.13-mmc-block-attach-partitions-fwnode-if-found-in-mmc-c.patch @@ -0,0 +1,104 @@ +From 45ff6c340ddfc2dade74d5b7a8962c778ab7042c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 3 Oct 2024 00:11:44 +0200 +Subject: [PATCH 4/5] mmc: block: attach partitions fwnode if found in mmc-card + +Attach partitions fwnode if found in mmc-card and register disk with it. + +This permits block partition to reference the node and register a +partition table defined in DT for the special case for embedded device +that doesn't have a partition table flashed but have an hardcoded +partition table passed from the system. + +JEDEC BOOT partition boot0/boot1 are supported but in DT we refer with +the JEDEC name of boot1 and boot2 to better adhere to documentation. + +Also JEDEC GP partition gp0/1/2/3 are supported but in DT we refer with +the JEDEC name of gp1/2/3/4 to better adhere to documentration. + +Signed-off-by: Christian Marangi +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/20241002221306.4403-5-ansuelsmth@gmail.com +Signed-off-by: Jens Axboe +--- + drivers/mmc/core/block.c | 55 +++++++++++++++++++++++++++++++++++++++- + 1 file changed, 54 insertions(+), 1 deletion(-) + +--- a/drivers/mmc/core/block.c ++++ b/drivers/mmc/core/block.c +@@ -2517,6 +2517,56 @@ static inline int mmc_blk_readonly(struc + !(card->csd.cmdclass & CCC_BLOCK_WRITE); + } + ++/* ++ * Search for a declared partitions node for the disk in mmc-card related node. ++ * ++ * This is to permit support for partition table defined in DT in special case ++ * where a partition table is not written in the disk and is expected to be ++ * passed from the running system. ++ * ++ * For the user disk, "partitions" node is searched. ++ * For the special HW disk, "partitions-" node with the appended name is used ++ * following this conversion table (to adhere to JEDEC naming) ++ * - boot0 -> partitions-boot1 ++ * - boot1 -> partitions-boot2 ++ * - gp0 -> partitions-gp1 ++ * - gp1 -> partitions-gp2 ++ * - gp2 -> partitions-gp3 ++ * - gp3 -> partitions-gp4 ++ */ ++static struct fwnode_handle *mmc_blk_get_partitions_node(struct device *mmc_dev, ++ const char *subname) ++{ ++ const char *node_name = "partitions"; ++ ++ if (subname) { ++ mmc_dev = mmc_dev->parent; ++ ++ /* ++ * Check if we are allocating a BOOT disk boot0/1 disk. ++ * In DT we use the JEDEC naming boot1/2. ++ */ ++ if (!strcmp(subname, "boot0")) ++ node_name = "partitions-boot1"; ++ if (!strcmp(subname, "boot1")) ++ node_name = "partitions-boot2"; ++ /* ++ * Check if we are allocating a GP disk gp0/1/2/3 disk. ++ * In DT we use the JEDEC naming gp1/2/3/4. ++ */ ++ if (!strcmp(subname, "gp0")) ++ node_name = "partitions-gp1"; ++ if (!strcmp(subname, "gp1")) ++ node_name = "partitions-gp2"; ++ if (!strcmp(subname, "gp2")) ++ node_name = "partitions-gp3"; ++ if (!strcmp(subname, "gp3")) ++ node_name = "partitions-gp4"; ++ } ++ ++ return device_get_named_child_node(mmc_dev, node_name); ++} ++ + static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, + struct device *parent, + sector_t size, +@@ -2525,6 +2575,7 @@ static struct mmc_blk_data *mmc_blk_allo + int area_type, + unsigned int part_type) + { ++ struct fwnode_handle *disk_fwnode; + struct mmc_blk_data *md; + int devidx, ret; + char cap_str[10]; +@@ -2626,7 +2677,9 @@ static struct mmc_blk_data *mmc_blk_allo + /* used in ->open, must be set before add_disk: */ + if (area_type == MMC_BLK_DATA_AREA_MAIN) + dev_set_drvdata(&card->dev, md); +- ret = device_add_disk(md->parent, md->disk, mmc_disk_attr_groups); ++ disk_fwnode = mmc_blk_get_partitions_node(parent, subname); ++ ret = add_disk_fwnode(md->parent, md->disk, mmc_disk_attr_groups, ++ disk_fwnode); + if (ret) + goto err_put_disk; + return md; diff --git a/target/linux/generic/backport-6.12/500-04-v6.13-block-add-support-for-partition-table-defined-in-OF.patch b/target/linux/generic/backport-6.12/500-04-v6.13-block-add-support-for-partition-table-defined-in-OF.patch new file mode 100644 index 00000000000..d260be168cd --- /dev/null +++ b/target/linux/generic/backport-6.12/500-04-v6.13-block-add-support-for-partition-table-defined-in-OF.patch @@ -0,0 +1,200 @@ +From 884555b557e5e6d41c866e2cd8d7b32f50ec974b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 3 Oct 2024 00:11:45 +0200 +Subject: [PATCH 5/5] block: add support for partition table defined in OF + +Add support for partition table defined in Device Tree. Similar to how +it's done with MTD, add support for defining a fixed partition table in +device tree. + +A common scenario for this is fixed block (eMMC) embedded devices that +have no MBR or GPT partition table to save storage space. Bootloader +access the block device with absolute address of data. + +This is to complete the functionality with an equivalent implementation +with providing partition table with bootargs, for case where the booargs +can't be modified and tweaking the Device Tree is the only solution to +have an usabe partition table. + +The implementation follow the fixed-partitions parser used on MTD +devices where a "partitions" node is expected to be declared with +"fixed-partitions" compatible in the OF node of the disk device +(mmc-card for eMMC for example) and each child node declare a label +and a reg with offset and size. If label is not declared, the node name +is used as fallback. Eventually is also possible to declare the read-only +property to flag the partition as read-only. + +Signed-off-by: Christian Marangi +Reviewed-by: Christoph Hellwig +Link: https://lore.kernel.org/r/20241002221306.4403-6-ansuelsmth@gmail.com +Signed-off-by: Jens Axboe +--- + block/partitions/Kconfig | 9 ++++ + block/partitions/Makefile | 1 + + block/partitions/check.h | 1 + + block/partitions/core.c | 3 ++ + block/partitions/of.c | 110 ++++++++++++++++++++++++++++++++++++++ + 5 files changed, 124 insertions(+) + create mode 100644 block/partitions/of.c + +--- a/block/partitions/Kconfig ++++ b/block/partitions/Kconfig +@@ -270,4 +270,13 @@ config CMDLINE_PARTITION + Say Y here if you want to read the partition table from bootargs. + The format for the command line is just like mtdparts. + ++config OF_PARTITION ++ bool "Device Tree partition support" if PARTITION_ADVANCED ++ depends on OF ++ help ++ Say Y here if you want to enable support for partition table ++ defined in Device Tree. (mainly for eMMC) ++ The format for the device tree node is just like MTD fixed-partition ++ schema. ++ + endmenu +--- a/block/partitions/Makefile ++++ b/block/partitions/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_CMDLINE_PARTITION) += cmdli + obj-$(CONFIG_MAC_PARTITION) += mac.o + obj-$(CONFIG_LDM_PARTITION) += ldm.o + obj-$(CONFIG_MSDOS_PARTITION) += msdos.o ++obj-$(CONFIG_OF_PARTITION) += of.o + obj-$(CONFIG_OSF_PARTITION) += osf.o + obj-$(CONFIG_SGI_PARTITION) += sgi.o + obj-$(CONFIG_SUN_PARTITION) += sun.o +--- a/block/partitions/check.h ++++ b/block/partitions/check.h +@@ -62,6 +62,7 @@ int karma_partition(struct parsed_partit + int ldm_partition(struct parsed_partitions *state); + int mac_partition(struct parsed_partitions *state); + int msdos_partition(struct parsed_partitions *state); ++int of_partition(struct parsed_partitions *state); + int osf_partition(struct parsed_partitions *state); + int sgi_partition(struct parsed_partitions *state); + int sun_partition(struct parsed_partitions *state); +--- a/block/partitions/core.c ++++ b/block/partitions/core.c +@@ -43,6 +43,9 @@ static int (*const check_part[])(struct + #ifdef CONFIG_CMDLINE_PARTITION + cmdline_partition, + #endif ++#ifdef CONFIG_OF_PARTITION ++ of_partition, /* cmdline have priority to OF */ ++#endif + #ifdef CONFIG_EFI_PARTITION + efi_partition, /* this must come before msdos */ + #endif +--- /dev/null ++++ b/block/partitions/of.c +@@ -0,0 +1,110 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++#include ++#include ++#include "check.h" ++ ++static int validate_of_partition(struct device_node *np, int slot) ++{ ++ u64 offset, size; ++ int len; ++ ++ const __be32 *reg = of_get_property(np, "reg", &len); ++ int a_cells = of_n_addr_cells(np); ++ int s_cells = of_n_size_cells(np); ++ ++ /* Make sure reg len match the expected addr and size cells */ ++ if (len / sizeof(*reg) != a_cells + s_cells) ++ return -EINVAL; ++ ++ /* Validate offset conversion from bytes to sectors */ ++ offset = of_read_number(reg, a_cells); ++ if (offset % SECTOR_SIZE) ++ return -EINVAL; ++ ++ /* Validate size conversion from bytes to sectors */ ++ size = of_read_number(reg + a_cells, s_cells); ++ if (!size || size % SECTOR_SIZE) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static void add_of_partition(struct parsed_partitions *state, int slot, ++ struct device_node *np) ++{ ++ struct partition_meta_info *info; ++ char tmp[sizeof(info->volname) + 4]; ++ const char *partname; ++ int len; ++ ++ const __be32 *reg = of_get_property(np, "reg", &len); ++ int a_cells = of_n_addr_cells(np); ++ int s_cells = of_n_size_cells(np); ++ ++ /* Convert bytes to sector size */ ++ u64 offset = of_read_number(reg, a_cells) / SECTOR_SIZE; ++ u64 size = of_read_number(reg + a_cells, s_cells) / SECTOR_SIZE; ++ ++ put_partition(state, slot, offset, size); ++ ++ if (of_property_read_bool(np, "read-only")) ++ state->parts[slot].flags |= ADDPART_FLAG_READONLY; ++ ++ /* ++ * Follow MTD label logic, search for label property, ++ * fallback to node name if not found. ++ */ ++ info = &state->parts[slot].info; ++ partname = of_get_property(np, "label", &len); ++ if (!partname) ++ partname = of_get_property(np, "name", &len); ++ strscpy(info->volname, partname, sizeof(info->volname)); ++ ++ snprintf(tmp, sizeof(tmp), "(%s)", info->volname); ++ strlcat(state->pp_buf, tmp, PAGE_SIZE); ++} ++ ++int of_partition(struct parsed_partitions *state) ++{ ++ struct device *ddev = disk_to_dev(state->disk); ++ struct device_node *np; ++ int slot; ++ ++ struct device_node *partitions_np = of_node_get(ddev->of_node); ++ ++ if (!partitions_np || ++ !of_device_is_compatible(partitions_np, "fixed-partitions")) ++ return 0; ++ ++ slot = 1; ++ /* Validate parition offset and size */ ++ for_each_child_of_node(partitions_np, np) { ++ if (validate_of_partition(np, slot)) { ++ of_node_put(np); ++ of_node_put(partitions_np); ++ ++ return -1; ++ } ++ ++ slot++; ++ } ++ ++ slot = 1; ++ for_each_child_of_node(partitions_np, np) { ++ if (slot >= state->limit) { ++ of_node_put(np); ++ break; ++ } ++ ++ add_of_partition(state, slot, np); ++ ++ slot++; ++ } ++ ++ strlcat(state->pp_buf, "\n", PAGE_SIZE); ++ ++ return 1; ++} diff --git a/target/linux/generic/backport-6.12/600-01-v6.13-net-phylink-move-manual-flow-control-setting.patch b/target/linux/generic/backport-6.12/600-01-v6.13-net-phylink-move-manual-flow-control-setting.patch new file mode 100644 index 00000000000..a9b86c25083 --- /dev/null +++ b/target/linux/generic/backport-6.12/600-01-v6.13-net-phylink-move-manual-flow-control-setting.patch @@ -0,0 +1,41 @@ +From 8cc5f4cb94c0b1c7c1ba8013c14fd02ffb1a25f3 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 8 Nov 2024 16:01:44 +0000 +Subject: [PATCH 1/5] net: phylink: move manual flow control setting + +Move the handling of manual flow control configuration to a common +location during resolve. We currently evaluate this for all but +fixed links. + +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1t9RQe-002Feh-T1@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1484,7 +1484,6 @@ static void phylink_resolve(struct work_ + switch (pl->cur_link_an_mode) { + case MLO_AN_PHY: + link_state = pl->phy_state; +- phylink_apply_manual_flow(pl, &link_state); + mac_config = link_state.link; + break; + +@@ -1545,11 +1544,13 @@ static void phylink_resolve(struct work_ + link_state.pause = pl->phy_state.pause; + mac_config = true; + } +- phylink_apply_manual_flow(pl, &link_state); + break; + } + } + ++ if (pl->cur_link_an_mode != MLO_AN_FIXED) ++ phylink_apply_manual_flow(pl, &link_state); ++ + if (mac_config) { + if (link_state.interface != pl->link_config.interface) { + /* The interface has changed, force the link down and diff --git a/target/linux/generic/backport-6.12/600-02-v6.13-net-phylink-move-MLO_AN_FIXED-resolve-handling-to-if.patch b/target/linux/generic/backport-6.12/600-02-v6.13-net-phylink-move-MLO_AN_FIXED-resolve-handling-to-if.patch new file mode 100644 index 00000000000..7ff091e85fa --- /dev/null +++ b/target/linux/generic/backport-6.12/600-02-v6.13-net-phylink-move-MLO_AN_FIXED-resolve-handling-to-if.patch @@ -0,0 +1,42 @@ +From 92abfcb4ced482afbe65d18980e6734fe1e62a34 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 8 Nov 2024 16:01:50 +0000 +Subject: [PATCH 2/5] net: phylink: move MLO_AN_FIXED resolve handling to if() + statement + +The switch() statement doesn't sit very well with the preceeding if() +statements, and results in excessive indentation that spoils code +readability. Begin cleaning this up by converting the MLO_AN_FIXED case +to an if() statement. + +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1t9RQk-002Fen-1A@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1480,6 +1480,9 @@ static void phylink_resolve(struct work_ + } else if (pl->link_failed) { + link_state.link = false; + retrigger = true; ++ } else if (pl->cur_link_an_mode == MLO_AN_FIXED) { ++ phylink_get_fixed_state(pl, &link_state); ++ mac_config = link_state.link; + } else { + switch (pl->cur_link_an_mode) { + case MLO_AN_PHY: +@@ -1487,11 +1490,6 @@ static void phylink_resolve(struct work_ + mac_config = link_state.link; + break; + +- case MLO_AN_FIXED: +- phylink_get_fixed_state(pl, &link_state); +- mac_config = link_state.link; +- break; +- + case MLO_AN_INBAND: + phylink_mac_pcs_get_state(pl, &link_state); + diff --git a/target/linux/generic/backport-6.12/600-03-v6.13-net-phylink-move-MLO_AN_PHY-resolve-handling-to-if-s.patch b/target/linux/generic/backport-6.12/600-03-v6.13-net-phylink-move-MLO_AN_PHY-resolve-handling-to-if-s.patch new file mode 100644 index 00000000000..76e76fbba1d --- /dev/null +++ b/target/linux/generic/backport-6.12/600-03-v6.13-net-phylink-move-MLO_AN_PHY-resolve-handling-to-if-s.patch @@ -0,0 +1,37 @@ +From f0f46c2a3d8ea9d1427298c8103a777d9e616c29 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 8 Nov 2024 16:01:55 +0000 +Subject: [PATCH 3/5] net: phylink: move MLO_AN_PHY resolve handling to if() + statement + +The switch() statement doesn't sit very well with the preceeding if() +statements, and results in excessive indentation that spoils code +readability. Continue cleaning this up by converting the MLO_AN_PHY +case to use an if() statmeent. + +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1t9RQp-002Fet-5W@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1483,13 +1483,11 @@ static void phylink_resolve(struct work_ + } else if (pl->cur_link_an_mode == MLO_AN_FIXED) { + phylink_get_fixed_state(pl, &link_state); + mac_config = link_state.link; ++ } else if (pl->cur_link_an_mode == MLO_AN_PHY) { ++ link_state = pl->phy_state; ++ mac_config = link_state.link; + } else { + switch (pl->cur_link_an_mode) { +- case MLO_AN_PHY: +- link_state = pl->phy_state; +- mac_config = link_state.link; +- break; +- + case MLO_AN_INBAND: + phylink_mac_pcs_get_state(pl, &link_state); + diff --git a/target/linux/generic/backport-6.12/600-04-v6.13-net-phylink-remove-switch-statement-in-resolve-handl.patch b/target/linux/generic/backport-6.12/600-04-v6.13-net-phylink-remove-switch-statement-in-resolve-handl.patch new file mode 100644 index 00000000000..989fc10b716 --- /dev/null +++ b/target/linux/generic/backport-6.12/600-04-v6.13-net-phylink-remove-switch-statement-in-resolve-handl.patch @@ -0,0 +1,127 @@ +From d1a16dbbd84e02d2a6dcfcb8d5c4b8b2c0289f00 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 8 Nov 2024 16:02:00 +0000 +Subject: [PATCH 4/5] net: phylink: remove switch() statement in resolve + handling + +The switch() statement doesn't sit very well with the preceeding if() +statements, so let's just convert everything to if()s. As a result of +the two preceding commits, there is now only one case in the switch() +statement. Remove the switch statement and reduce the code indentation. +Code reformatting will be in the following commit. + +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1t9RQu-002Fez-AA@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 94 +++++++++++++++++++-------------------- + 1 file changed, 45 insertions(+), 49 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1487,60 +1487,56 @@ static void phylink_resolve(struct work_ + link_state = pl->phy_state; + mac_config = link_state.link; + } else { +- switch (pl->cur_link_an_mode) { +- case MLO_AN_INBAND: +- phylink_mac_pcs_get_state(pl, &link_state); +- +- /* The PCS may have a latching link-fail indicator. +- * If the link was up, bring the link down and +- * re-trigger the resolve. Otherwise, re-read the +- * PCS state to get the current status of the link. ++ phylink_mac_pcs_get_state(pl, &link_state); ++ ++ /* The PCS may have a latching link-fail indicator. ++ * If the link was up, bring the link down and ++ * re-trigger the resolve. Otherwise, re-read the ++ * PCS state to get the current status of the link. ++ */ ++ if (!link_state.link) { ++ if (cur_link_state) ++ retrigger = true; ++ else ++ phylink_mac_pcs_get_state(pl, ++ &link_state); ++ } ++ ++ /* If we have a phy, the "up" state is the union of ++ * both the PHY and the MAC ++ */ ++ if (pl->phydev) ++ link_state.link &= pl->phy_state.link; ++ ++ /* Only update if the PHY link is up */ ++ if (pl->phydev && pl->phy_state.link) { ++ /* If the interface has changed, force a ++ * link down event if the link isn't already ++ * down, and re-resolve. + */ +- if (!link_state.link) { +- if (cur_link_state) +- retrigger = true; +- else +- phylink_mac_pcs_get_state(pl, +- &link_state); ++ if (link_state.interface != ++ pl->phy_state.interface) { ++ retrigger = true; ++ link_state.link = false; + } ++ link_state.interface = pl->phy_state.interface; + +- /* If we have a phy, the "up" state is the union of +- * both the PHY and the MAC ++ /* If we are doing rate matching, then the ++ * link speed/duplex comes from the PHY + */ +- if (pl->phydev) +- link_state.link &= pl->phy_state.link; +- +- /* Only update if the PHY link is up */ +- if (pl->phydev && pl->phy_state.link) { +- /* If the interface has changed, force a +- * link down event if the link isn't already +- * down, and re-resolve. +- */ +- if (link_state.interface != +- pl->phy_state.interface) { +- retrigger = true; +- link_state.link = false; +- } +- link_state.interface = pl->phy_state.interface; +- +- /* If we are doing rate matching, then the +- * link speed/duplex comes from the PHY +- */ +- if (pl->phy_state.rate_matching) { +- link_state.rate_matching = +- pl->phy_state.rate_matching; +- link_state.speed = pl->phy_state.speed; +- link_state.duplex = +- pl->phy_state.duplex; +- } +- +- /* If we have a PHY, we need to update with +- * the PHY flow control bits. +- */ +- link_state.pause = pl->phy_state.pause; +- mac_config = true; ++ if (pl->phy_state.rate_matching) { ++ link_state.rate_matching = ++ pl->phy_state.rate_matching; ++ link_state.speed = pl->phy_state.speed; ++ link_state.duplex = ++ pl->phy_state.duplex; + } +- break; ++ ++ /* If we have a PHY, we need to update with ++ * the PHY flow control bits. ++ */ ++ link_state.pause = pl->phy_state.pause; ++ mac_config = true; + } + } + diff --git a/target/linux/generic/backport-6.12/600-05-v6.13-net-phylink-clean-up-phylink_resolve.patch b/target/linux/generic/backport-6.12/600-05-v6.13-net-phylink-clean-up-phylink_resolve.patch new file mode 100644 index 00000000000..285eea862eb --- /dev/null +++ b/target/linux/generic/backport-6.12/600-05-v6.13-net-phylink-clean-up-phylink_resolve.patch @@ -0,0 +1,85 @@ +From bc08ce37d99a3992e975a0f397503cb23404f25a Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 8 Nov 2024 16:02:05 +0000 +Subject: [PATCH 5/5] net: phylink: clean up phylink_resolve() + +Now that we have reduced the indentation level, clean up the code +formatting. + +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1t9RQz-002Ff5-EA@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 35 ++++++++++++++++------------------- + 1 file changed, 16 insertions(+), 19 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1489,51 +1489,48 @@ static void phylink_resolve(struct work_ + } else { + phylink_mac_pcs_get_state(pl, &link_state); + +- /* The PCS may have a latching link-fail indicator. +- * If the link was up, bring the link down and +- * re-trigger the resolve. Otherwise, re-read the +- * PCS state to get the current status of the link. ++ /* The PCS may have a latching link-fail indicator. If the link ++ * was up, bring the link down and re-trigger the resolve. ++ * Otherwise, re-read the PCS state to get the current status ++ * of the link. + */ + if (!link_state.link) { + if (cur_link_state) + retrigger = true; + else +- phylink_mac_pcs_get_state(pl, +- &link_state); ++ phylink_mac_pcs_get_state(pl, &link_state); + } + +- /* If we have a phy, the "up" state is the union of +- * both the PHY and the MAC ++ /* If we have a phy, the "up" state is the union of both the ++ * PHY and the MAC + */ + if (pl->phydev) + link_state.link &= pl->phy_state.link; + + /* Only update if the PHY link is up */ + if (pl->phydev && pl->phy_state.link) { +- /* If the interface has changed, force a +- * link down event if the link isn't already +- * down, and re-resolve. ++ /* If the interface has changed, force a link down ++ * event if the link isn't already down, and re-resolve. + */ +- if (link_state.interface != +- pl->phy_state.interface) { ++ if (link_state.interface != pl->phy_state.interface) { + retrigger = true; + link_state.link = false; + } ++ + link_state.interface = pl->phy_state.interface; + +- /* If we are doing rate matching, then the +- * link speed/duplex comes from the PHY ++ /* If we are doing rate matching, then the link ++ * speed/duplex comes from the PHY + */ + if (pl->phy_state.rate_matching) { + link_state.rate_matching = + pl->phy_state.rate_matching; + link_state.speed = pl->phy_state.speed; +- link_state.duplex = +- pl->phy_state.duplex; ++ link_state.duplex = pl->phy_state.duplex; + } + +- /* If we have a PHY, we need to update with +- * the PHY flow control bits. ++ /* If we have a PHY, we need to update with the PHY ++ * flow control bits. + */ + link_state.pause = pl->phy_state.pause; + mac_config = true; diff --git a/target/linux/generic/backport-6.12/601-01-v6.14-net-phylink-pass-phylink-and-pcs-into-phylink_pcs_ne.patch b/target/linux/generic/backport-6.12/601-01-v6.14-net-phylink-pass-phylink-and-pcs-into-phylink_pcs_ne.patch new file mode 100644 index 00000000000..6868cd1518f --- /dev/null +++ b/target/linux/generic/backport-6.12/601-01-v6.14-net-phylink-pass-phylink-and-pcs-into-phylink_pcs_ne.patch @@ -0,0 +1,95 @@ +From 17ed1911f9c8d4f9af8e13b2c95103ee06dadc0f Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:30:47 +0000 +Subject: [PATCH 01/13] net: phylink: pass phylink and pcs into + phylink_pcs_neg_mode() + +Move the call to phylink_pcs_neg_mode() in phylink_major_config() after +we have selected the appropriate PCS to allow the PCS to be passed in. + +Add struct phylink and struct phylink_pcs pointers to +phylink_pcs_neg_mode() and pass in the appropriate structures. Set +pl->pcs_neg_mode before returning, and remove the return value. + +This will allow the capabilities of the PCS and any PHY to be used when +deciding which pcs_neg_mode should be used. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUrP-006ITh-6u@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1102,7 +1102,8 @@ static void phylink_pcs_an_restart(struc + + /** + * phylink_pcs_neg_mode() - helper to determine PCS inband mode +- * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. ++ * @pl: a pointer to a &struct phylink returned from phylink_create() ++ * @pcs: a pointer to &struct phylink_pcs + * @interface: interface mode to be used + * @advertising: adertisement ethtool link mode mask + * +@@ -1119,11 +1120,13 @@ static void phylink_pcs_an_restart(struc + * Note: this is for cases where the PCS itself is involved in negotiation + * (e.g. Clause 37, SGMII and similar) not Clause 73. + */ +-static unsigned int phylink_pcs_neg_mode(unsigned int mode, +- phy_interface_t interface, +- const unsigned long *advertising) ++static void phylink_pcs_neg_mode(struct phylink *pl, struct phylink_pcs *pcs, ++ phy_interface_t interface, ++ const unsigned long *advertising) + { +- unsigned int neg_mode; ++ unsigned int neg_mode, mode; ++ ++ mode = pl->cur_link_an_mode; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: +@@ -1164,7 +1167,7 @@ static unsigned int phylink_pcs_neg_mode + break; + } + +- return neg_mode; ++ pl->pcs_neg_mode = neg_mode; + } + + static void phylink_major_config(struct phylink *pl, bool restart, +@@ -1178,10 +1181,6 @@ static void phylink_major_config(struct + + phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); + +- pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode, +- state->interface, +- state->advertising); +- + if (pl->using_mac_select_pcs) { + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); + if (IS_ERR(pcs)) { +@@ -1194,6 +1193,8 @@ static void phylink_major_config(struct + pcs_changed = pcs && pl->pcs != pcs; + } + ++ phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising); ++ + phylink_pcs_poll_stop(pl); + + if (pl->mac_ops->mac_prepare) { +@@ -1284,9 +1285,8 @@ static int phylink_change_inband_advert( + pl->link_config.pause); + + /* Recompute the PCS neg mode */ +- pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode, +- pl->link_config.interface, +- pl->link_config.advertising); ++ phylink_pcs_neg_mode(pl, pl->pcs, pl->link_config.interface, ++ pl->link_config.advertising); + + neg_mode = pl->cur_link_an_mode; + if (pl->pcs->neg_mode) diff --git a/target/linux/generic/backport-6.12/601-02-v6.14-net-phylink-split-cur_link_an_mode-into-requested-an.patch b/target/linux/generic/backport-6.12/601-02-v6.14-net-phylink-split-cur_link_an_mode-into-requested-an.patch new file mode 100644 index 00000000000..76f4d5a48bf --- /dev/null +++ b/target/linux/generic/backport-6.12/601-02-v6.14-net-phylink-split-cur_link_an_mode-into-requested-an.patch @@ -0,0 +1,290 @@ +From 1f92ead7e15003f632b5f138e8138095e0997d3d Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:30:52 +0000 +Subject: [PATCH 02/13] net: phylink: split cur_link_an_mode into requested and + active + +There is an interdependence between the current link_an_mode and +pcs_neg_mode that some drivers rely upon to know whether inband or PHY +mode will be used. + +In order to support detection of PCS and PHY inband capabilities +resulting in automatic selection of inband or PHY mode, we need to +cater for this, and support changing the MAC link_an_mode. However, we +end up with an inter-dependency between the current link_an_mode and +pcs_neg_mode. + +To solve this, split the current link_an_mode into the requested +link_an_mode and active link_an_mode. The requested link_an_mode will +always be passed to phylink_pcs_neg_mode(), and the active link_an_mode +will be used for everything else, and only updated during +phylink_major_config(). This will ensure that phylink_pcs_neg_mode()'s +link_an_mode will not depend on the active link_an_mode that will, +in a future patch, depend on pcs_neg_mode. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUrU-006ITn-Ai@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 60 ++++++++++++++++++++------------------- + 1 file changed, 31 insertions(+), 29 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -56,7 +56,8 @@ struct phylink { + struct phy_device *phydev; + phy_interface_t link_interface; /* PHY_INTERFACE_xxx */ + u8 cfg_link_an_mode; /* MLO_AN_xxx */ +- u8 cur_link_an_mode; ++ u8 req_link_an_mode; /* Requested MLO_AN_xxx mode */ ++ u8 act_link_an_mode; /* Active MLO_AN_xxx mode */ + u8 link_port; /* The current non-phy ethtool port */ + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + +@@ -1082,13 +1083,13 @@ static void phylink_mac_config(struct ph + + phylink_dbg(pl, + "%s: mode=%s/%s/%s adv=%*pb pause=%02x\n", +- __func__, phylink_an_mode_str(pl->cur_link_an_mode), ++ __func__, phylink_an_mode_str(pl->act_link_an_mode), + phy_modes(st.interface), + phy_rate_matching_to_str(st.rate_matching), + __ETHTOOL_LINK_MODE_MASK_NBITS, st.advertising, + st.pause); + +- pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, &st); ++ pl->mac_ops->mac_config(pl->config, pl->act_link_an_mode, &st); + } + + static void phylink_pcs_an_restart(struct phylink *pl) +@@ -1096,7 +1097,7 @@ static void phylink_pcs_an_restart(struc + if (pl->pcs && linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + pl->link_config.advertising) && + phy_interface_mode_is_8023z(pl->link_config.interface) && +- phylink_autoneg_inband(pl->cur_link_an_mode)) ++ phylink_autoneg_inband(pl->act_link_an_mode)) + pl->pcs->ops->pcs_an_restart(pl->pcs); + } + +@@ -1126,7 +1127,7 @@ static void phylink_pcs_neg_mode(struct + { + unsigned int neg_mode, mode; + +- mode = pl->cur_link_an_mode; ++ mode = pl->req_link_an_mode; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: +@@ -1168,6 +1169,7 @@ static void phylink_pcs_neg_mode(struct + } + + pl->pcs_neg_mode = neg_mode; ++ pl->act_link_an_mode = mode; + } + + static void phylink_major_config(struct phylink *pl, bool restart, +@@ -1198,7 +1200,7 @@ static void phylink_major_config(struct + phylink_pcs_poll_stop(pl); + + if (pl->mac_ops->mac_prepare) { +- err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode, ++ err = pl->mac_ops->mac_prepare(pl->config, pl->act_link_an_mode, + state->interface); + if (err < 0) { + phylink_err(pl, "mac_prepare failed: %pe\n", +@@ -1232,7 +1234,7 @@ static void phylink_major_config(struct + if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed) + phylink_pcs_enable(pl->pcs); + +- neg_mode = pl->cur_link_an_mode; ++ neg_mode = pl->act_link_an_mode; + if (pl->pcs && pl->pcs->neg_mode) + neg_mode = pl->pcs_neg_mode; + +@@ -1248,7 +1250,7 @@ static void phylink_major_config(struct + phylink_pcs_an_restart(pl); + + if (pl->mac_ops->mac_finish) { +- err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode, ++ err = pl->mac_ops->mac_finish(pl->config, pl->act_link_an_mode, + state->interface); + if (err < 0) + phylink_err(pl, "mac_finish failed: %pe\n", +@@ -1279,7 +1281,7 @@ static int phylink_change_inband_advert( + return 0; + + phylink_dbg(pl, "%s: mode=%s/%s adv=%*pb pause=%02x\n", __func__, +- phylink_an_mode_str(pl->cur_link_an_mode), ++ phylink_an_mode_str(pl->req_link_an_mode), + phy_modes(pl->link_config.interface), + __ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising, + pl->link_config.pause); +@@ -1288,7 +1290,7 @@ static int phylink_change_inband_advert( + phylink_pcs_neg_mode(pl, pl->pcs, pl->link_config.interface, + pl->link_config.advertising); + +- neg_mode = pl->cur_link_an_mode; ++ neg_mode = pl->act_link_an_mode; + if (pl->pcs->neg_mode) + neg_mode = pl->pcs_neg_mode; + +@@ -1353,7 +1355,7 @@ static void phylink_mac_initial_config(s + { + struct phylink_link_state link_state; + +- switch (pl->cur_link_an_mode) { ++ switch (pl->req_link_an_mode) { + case MLO_AN_PHY: + link_state = pl->phy_state; + break; +@@ -1427,14 +1429,14 @@ static void phylink_link_up(struct phyli + + pl->cur_interface = link_state.interface; + +- neg_mode = pl->cur_link_an_mode; ++ neg_mode = pl->act_link_an_mode; + if (pl->pcs && pl->pcs->neg_mode) + neg_mode = pl->pcs_neg_mode; + + phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed, + duplex); + +- pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode, ++ pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->act_link_an_mode, + pl->cur_interface, speed, duplex, + !!(link_state.pause & MLO_PAUSE_TX), rx_pause); + +@@ -1454,7 +1456,7 @@ static void phylink_link_down(struct phy + + if (ndev) + netif_carrier_off(ndev); +- pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode, ++ pl->mac_ops->mac_link_down(pl->config, pl->act_link_an_mode, + pl->cur_interface); + phylink_info(pl, "Link is Down\n"); + } +@@ -1480,10 +1482,10 @@ static void phylink_resolve(struct work_ + } else if (pl->link_failed) { + link_state.link = false; + retrigger = true; +- } else if (pl->cur_link_an_mode == MLO_AN_FIXED) { ++ } else if (pl->act_link_an_mode == MLO_AN_FIXED) { + phylink_get_fixed_state(pl, &link_state); + mac_config = link_state.link; +- } else if (pl->cur_link_an_mode == MLO_AN_PHY) { ++ } else if (pl->act_link_an_mode == MLO_AN_PHY) { + link_state = pl->phy_state; + mac_config = link_state.link; + } else { +@@ -1537,7 +1539,7 @@ static void phylink_resolve(struct work_ + } + } + +- if (pl->cur_link_an_mode != MLO_AN_FIXED) ++ if (pl->act_link_an_mode != MLO_AN_FIXED) + phylink_apply_manual_flow(pl, &link_state); + + if (mac_config) { +@@ -1661,7 +1663,7 @@ int phylink_set_fixed_link(struct phylin + pl->link_config.an_complete = 1; + + pl->cfg_link_an_mode = MLO_AN_FIXED; +- pl->cur_link_an_mode = pl->cfg_link_an_mode; ++ pl->req_link_an_mode = pl->cfg_link_an_mode; + + return 0; + } +@@ -1756,7 +1758,7 @@ struct phylink *phylink_create(struct ph + } + } + +- pl->cur_link_an_mode = pl->cfg_link_an_mode; ++ pl->req_link_an_mode = pl->cfg_link_an_mode; + + ret = phylink_register_sfp(pl, fwnode); + if (ret < 0) { +@@ -2213,7 +2215,7 @@ void phylink_start(struct phylink *pl) + ASSERT_RTNL(); + + phylink_info(pl, "configuring for %s/%s link mode\n", +- phylink_an_mode_str(pl->cur_link_an_mode), ++ phylink_an_mode_str(pl->req_link_an_mode), + phy_modes(pl->link_config.interface)); + + /* Always set the carrier off */ +@@ -2472,7 +2474,7 @@ int phylink_ethtool_ksettings_get(struct + + linkmode_copy(kset->link_modes.supported, pl->supported); + +- switch (pl->cur_link_an_mode) { ++ switch (pl->act_link_an_mode) { + case MLO_AN_FIXED: + /* We are using fixed settings. Report these as the + * current link settings - and note that these also +@@ -2564,7 +2566,7 @@ int phylink_ethtool_ksettings_set(struct + /* If we have a fixed link, refuse to change link parameters. + * If the link parameters match, accept them but do nothing. + */ +- if (pl->cur_link_an_mode == MLO_AN_FIXED) { ++ if (pl->req_link_an_mode == MLO_AN_FIXED) { + if (s->speed != pl->link_config.speed || + s->duplex != pl->link_config.duplex) + return -EINVAL; +@@ -2580,7 +2582,7 @@ int phylink_ethtool_ksettings_set(struct + * is our default case) but do not allow the advertisement to + * be changed. If the advertisement matches, simply return. + */ +- if (pl->cur_link_an_mode == MLO_AN_FIXED) { ++ if (pl->req_link_an_mode == MLO_AN_FIXED) { + if (!linkmode_equal(config.advertising, + pl->link_config.advertising)) + return -EINVAL; +@@ -2620,7 +2622,7 @@ int phylink_ethtool_ksettings_set(struct + linkmode_copy(support, pl->supported); + if (phylink_validate(pl, support, &config)) { + phylink_err(pl, "validation of %s/%s with support %*pb failed\n", +- phylink_an_mode_str(pl->cur_link_an_mode), ++ phylink_an_mode_str(pl->req_link_an_mode), + phy_modes(config.interface), + __ETHTOOL_LINK_MODE_MASK_NBITS, support); + return -EINVAL; +@@ -2720,7 +2722,7 @@ int phylink_ethtool_set_pauseparam(struc + + ASSERT_RTNL(); + +- if (pl->cur_link_an_mode == MLO_AN_FIXED) ++ if (pl->req_link_an_mode == MLO_AN_FIXED) + return -EOPNOTSUPP; + + if (!phylink_test(pl->supported, Pause) && +@@ -2984,7 +2986,7 @@ static int phylink_mii_read(struct phyli + struct phylink_link_state state; + int val = 0xffff; + +- switch (pl->cur_link_an_mode) { ++ switch (pl->act_link_an_mode) { + case MLO_AN_FIXED: + if (phy_id == 0) { + phylink_get_fixed_state(pl, &state); +@@ -3009,7 +3011,7 @@ static int phylink_mii_read(struct phyli + static int phylink_mii_write(struct phylink *pl, unsigned int phy_id, + unsigned int reg, unsigned int val) + { +- switch (pl->cur_link_an_mode) { ++ switch (pl->act_link_an_mode) { + case MLO_AN_FIXED: + break; + +@@ -3199,9 +3201,9 @@ static void phylink_sfp_set_config(struc + changed = true; + } + +- if (pl->cur_link_an_mode != mode || ++ if (pl->req_link_an_mode != mode || + pl->link_config.interface != state->interface) { +- pl->cur_link_an_mode = mode; ++ pl->req_link_an_mode = mode; + pl->link_config.interface = state->interface; + + changed = true; diff --git a/target/linux/generic/backport-6.12/601-03-v6.14-net-phylink-add-debug-for-phylink_major_config.patch b/target/linux/generic/backport-6.12/601-03-v6.14-net-phylink-add-debug-for-phylink_major_config.patch new file mode 100644 index 00000000000..ee625c74861 --- /dev/null +++ b/target/linux/generic/backport-6.12/601-03-v6.14-net-phylink-add-debug-for-phylink_major_config.patch @@ -0,0 +1,66 @@ +From 4e7d000286fe8e12f2d88032711ffab3ab658b12 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:30:57 +0000 +Subject: [PATCH 03/13] net: phylink: add debug for phylink_major_config() + +Now that we have a more complexity in phylink_major_config(), augment +the debugging so we can see what's going on there. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUrZ-006ITt-Fa@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -176,6 +176,24 @@ static const char *phylink_an_mode_str(u + return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; + } + ++static const char *phylink_pcs_mode_str(unsigned int mode) ++{ ++ if (!mode) ++ return "none"; ++ ++ if (mode & PHYLINK_PCS_NEG_OUTBAND) ++ return "outband"; ++ ++ if (mode & PHYLINK_PCS_NEG_INBAND) { ++ if (mode & PHYLINK_PCS_NEG_ENABLED) ++ return "inband,an-enabled"; ++ else ++ return "inband,an-disabled"; ++ } ++ ++ return "unknown"; ++} ++ + static unsigned int phylink_interface_signal_rate(phy_interface_t interface) + { + switch (interface) { +@@ -1181,7 +1199,9 @@ static void phylink_major_config(struct + unsigned int neg_mode; + int err; + +- phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); ++ phylink_dbg(pl, "major config, requested %s/%s\n", ++ phylink_an_mode_str(pl->req_link_an_mode), ++ phy_modes(state->interface)); + + if (pl->using_mac_select_pcs) { + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); +@@ -1197,6 +1217,11 @@ static void phylink_major_config(struct + + phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising); + ++ phylink_dbg(pl, "major config, active %s/%s/%s\n", ++ phylink_an_mode_str(pl->act_link_an_mode), ++ phylink_pcs_mode_str(pl->pcs_neg_mode), ++ phy_modes(state->interface)); ++ + phylink_pcs_poll_stop(pl); + + if (pl->mac_ops->mac_prepare) { diff --git a/target/linux/generic/backport-6.12/601-04-v6.14-net-phy-add-phy_inband_caps.patch b/target/linux/generic/backport-6.12/601-04-v6.14-net-phy-add-phy_inband_caps.patch new file mode 100644 index 00000000000..0cd38f086cf --- /dev/null +++ b/target/linux/generic/backport-6.12/601-04-v6.14-net-phy-add-phy_inband_caps.patch @@ -0,0 +1,118 @@ +From b4c7698dd95f253c6958d8c6ac219098009bf28a Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:31:02 +0000 +Subject: [PATCH 04/13] net: phy: add phy_inband_caps() + +Add a method to query the PHY's in-band capabilities for a PHY +interface mode. + +Where the interface mode does not have in-band capability, or the PHY +driver has not been updated to return this information, then +phy_inband_caps() should return zero. Otherwise, PHY drivers will +return a value consisting of the following flags: + +LINK_INBAND_DISABLE indicates that the hardware does not support +in-band signalling, or can have in-band signalling configured via +software to be disabled. + +LINK_INBAND_ENABLE indicates that the hardware will use in-band +signalling, or can have in-band signalling configured via software +to be enabled. + +LINK_INBAND_BYPASS indicates that the hardware has the ability to +bypass in-band signalling when enabled after a timeout if the link +partner does not respond to its in-band signalling. + +This reports the PHY capabilities for the particular interface mode, +not the current configuration. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUre-006ITz-KF@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy.c | 21 +++++++++++++++++++++ + include/linux/phy.h | 28 ++++++++++++++++++++++++++++ + 2 files changed, 49 insertions(+) + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -1049,6 +1049,27 @@ static int phy_check_link_status(struct + } + + /** ++ * phy_inband_caps - query which in-band signalling modes are supported ++ * @phydev: a pointer to a &struct phy_device ++ * @interface: the interface mode for the PHY ++ * ++ * Returns zero if it is unknown what in-band signalling is supported by the ++ * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise, ++ * returns a bit mask of the LINK_INBAND_* values from ++ * &enum link_inband_signalling to describe which inband modes are supported ++ * by the PHY for this interface mode. ++ */ ++unsigned int phy_inband_caps(struct phy_device *phydev, ++ phy_interface_t interface) ++{ ++ if (phydev->drv && phydev->drv->inband_caps) ++ return phydev->drv->inband_caps(phydev, interface); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(phy_inband_caps); ++ ++/** + * _phy_start_aneg - start auto-negotiation for this PHY device + * @phydev: the phy_device struct + * +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -826,6 +826,24 @@ struct phy_tdr_config { + #define PHY_PAIR_ALL -1 + + /** ++ * enum link_inband_signalling - in-band signalling modes that are supported ++ * ++ * @LINK_INBAND_DISABLE: in-band signalling can be disabled ++ * @LINK_INBAND_ENABLE: in-band signalling can be enabled without bypass ++ * @LINK_INBAND_BYPASS: in-band signalling can be enabled with bypass ++ * ++ * The possible and required bits can only be used if the valid bit is set. ++ * If possible is clear, that means inband signalling can not be used. ++ * Required is only valid when possible is set, and means that inband ++ * signalling must be used. ++ */ ++enum link_inband_signalling { ++ LINK_INBAND_DISABLE = BIT(0), ++ LINK_INBAND_ENABLE = BIT(1), ++ LINK_INBAND_BYPASS = BIT(2), ++}; ++ ++/** + * struct phy_plca_cfg - Configuration of the PLCA (Physical Layer Collision + * Avoidance) Reconciliation Sublayer. + * +@@ -964,6 +982,14 @@ struct phy_driver { + int (*get_features)(struct phy_device *phydev); + + /** ++ * @inband_caps: query whether in-band is supported for the given PHY ++ * interface mode. Returns a bitmask of bits defined by enum ++ * link_inband_signalling. ++ */ ++ unsigned int (*inband_caps)(struct phy_device *phydev, ++ phy_interface_t interface); ++ ++ /** + * @get_rate_matching: Get the supported type of rate matching for a + * particular phy interface. This is used by phy consumers to determine + * whether to advertise lower-speed modes for that interface. It is +@@ -1842,6 +1868,8 @@ int phy_config_aneg(struct phy_device *p + int _phy_start_aneg(struct phy_device *phydev); + int phy_start_aneg(struct phy_device *phydev); + int phy_aneg_done(struct phy_device *phydev); ++unsigned int phy_inband_caps(struct phy_device *phydev, ++ phy_interface_t interface); + int phy_speed_down(struct phy_device *phydev, bool sync); + int phy_speed_up(struct phy_device *phydev); + bool phy_check_valid(int speed, int duplex, unsigned long *features); diff --git a/target/linux/generic/backport-6.12/601-05-v6.14-net-phy-bcm84881-implement-phy_inband_caps-method.patch b/target/linux/generic/backport-6.12/601-05-v6.14-net-phy-bcm84881-implement-phy_inband_caps-method.patch new file mode 100644 index 00000000000..0ba3cd28022 --- /dev/null +++ b/target/linux/generic/backport-6.12/601-05-v6.14-net-phy-bcm84881-implement-phy_inband_caps-method.patch @@ -0,0 +1,41 @@ +From c64c7fa0a774d9da72071a8517e359992baac982 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:31:07 +0000 +Subject: [PATCH 05/13] net: phy: bcm84881: implement phy_inband_caps() method + +BCM84881 has no support for inband signalling, so this is a trivial +implementation that returns no support for inband. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Acked-by: Florian Fainelli +Link: https://patch.msgid.link/E1tIUrj-006IU6-ON@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/bcm84881.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/net/phy/bcm84881.c ++++ b/drivers/net/phy/bcm84881.c +@@ -235,11 +235,21 @@ static int bcm84881_read_status(struct p + return genphy_c45_read_mdix(phydev); + } + ++/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII ++ * or 802.3z control word, so inband will not work. ++ */ ++static unsigned int bcm84881_inband_caps(struct phy_device *phydev, ++ phy_interface_t interface) ++{ ++ return LINK_INBAND_DISABLE; ++} ++ + static struct phy_driver bcm84881_drivers[] = { + { + .phy_id = 0xae025150, + .phy_id_mask = 0xfffffff0, + .name = "Broadcom BCM84881", ++ .inband_caps = bcm84881_inband_caps, + .config_init = bcm84881_config_init, + .probe = bcm84881_probe, + .get_features = bcm84881_get_features, diff --git a/target/linux/generic/backport-6.12/601-06-v6.14-net-phy-marvell-implement-phy_inband_caps-method.patch b/target/linux/generic/backport-6.12/601-06-v6.14-net-phy-marvell-implement-phy_inband_caps-method.patch new file mode 100644 index 00000000000..e2dfeba1d55 --- /dev/null +++ b/target/linux/generic/backport-6.12/601-06-v6.14-net-phy-marvell-implement-phy_inband_caps-method.patch @@ -0,0 +1,63 @@ +From 1c86828dff88e28b8ade6bddeee0163a023faf91 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:31:12 +0000 +Subject: [PATCH 06/13] net: phy: marvell: implement phy_inband_caps() method + +Provide an implementation for phy_inband_caps() for Marvell PHYs used +on SFP modules, so that phylink knows the PHYs capabilities. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUro-006IUC-Rq@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/marvell.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -716,6 +716,20 @@ static int marvell_config_aneg_fiber(str + return genphy_check_and_restart_aneg(phydev, changed); + } + ++static unsigned int m88e1111_inband_caps(struct phy_device *phydev, ++ phy_interface_t interface) ++{ ++ /* In 1000base-X and SGMII modes, the inband mode can be changed ++ * through the Fibre page BMCR ANENABLE bit. ++ */ ++ if (interface == PHY_INTERFACE_MODE_1000BASEX || ++ interface == PHY_INTERFACE_MODE_SGMII) ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE | ++ LINK_INBAND_BYPASS; ++ ++ return 0; ++} ++ + static int m88e1111_config_aneg(struct phy_device *phydev) + { + int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR); +@@ -3704,6 +3718,7 @@ static struct phy_driver marvell_drivers + .name = "Marvell 88E1112", + /* PHY_GBIT_FEATURES */ + .probe = marvell_probe, ++ .inband_caps = m88e1111_inband_caps, + .config_init = m88e1112_config_init, + .config_aneg = marvell_config_aneg, + .config_intr = marvell_config_intr, +@@ -3725,6 +3740,7 @@ static struct phy_driver marvell_drivers + /* PHY_GBIT_FEATURES */ + .flags = PHY_POLL_CABLE_TEST, + .probe = marvell_probe, ++ .inband_caps = m88e1111_inband_caps, + .config_init = m88e1111gbe_config_init, + .config_aneg = m88e1111_config_aneg, + .read_status = marvell_read_status, +@@ -3748,6 +3764,7 @@ static struct phy_driver marvell_drivers + .name = "Marvell 88E1111 (Finisar)", + /* PHY_GBIT_FEATURES */ + .probe = marvell_probe, ++ .inband_caps = m88e1111_inband_caps, + .config_init = m88e1111gbe_config_init, + .config_aneg = m88e1111_config_aneg, + .read_status = marvell_read_status, diff --git a/target/linux/generic/backport-6.12/601-07-v6.14-net-phy-add-phy_config_inband.patch b/target/linux/generic/backport-6.12/601-07-v6.14-net-phy-add-phy_config_inband.patch new file mode 100644 index 00000000000..d667bcbec05 --- /dev/null +++ b/target/linux/generic/backport-6.12/601-07-v6.14-net-phy-add-phy_config_inband.patch @@ -0,0 +1,79 @@ +From 5d58a890c02770ba8d790b1f3c6e8c0e20514dc2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:31:18 +0000 +Subject: [PATCH 07/13] net: phy: add phy_config_inband() + +Add a method to configure the PHY's in-band mode. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUru-006IUI-08@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy.c | 32 ++++++++++++++++++++++++++++++++ + include/linux/phy.h | 6 ++++++ + 2 files changed, 38 insertions(+) + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -1070,6 +1070,38 @@ unsigned int phy_inband_caps(struct phy_ + EXPORT_SYMBOL_GPL(phy_inband_caps); + + /** ++ * phy_config_inband - configure the desired PHY in-band mode ++ * @phydev: the phy_device struct ++ * @modes: in-band modes to configure ++ * ++ * Description: disables, enables or enables-with-bypass in-band signalling ++ * between the PHY and host system. ++ * ++ * Returns: zero on success, or negative errno value. ++ */ ++int phy_config_inband(struct phy_device *phydev, unsigned int modes) ++{ ++ int err; ++ ++ if (!!(modes & LINK_INBAND_DISABLE) + ++ !!(modes & LINK_INBAND_ENABLE) + ++ !!(modes & LINK_INBAND_BYPASS) != 1) ++ return -EINVAL; ++ ++ mutex_lock(&phydev->lock); ++ if (!phydev->drv) ++ err = -EIO; ++ else if (!phydev->drv->config_inband) ++ err = -EOPNOTSUPP; ++ else ++ err = phydev->drv->config_inband(phydev, modes); ++ mutex_unlock(&phydev->lock); ++ ++ return err; ++} ++EXPORT_SYMBOL(phy_config_inband); ++ ++/** + * _phy_start_aneg - start auto-negotiation for this PHY device + * @phydev: the phy_device struct + * +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -990,6 +990,11 @@ struct phy_driver { + phy_interface_t interface); + + /** ++ * @config_inband: configure in-band mode for the PHY ++ */ ++ int (*config_inband)(struct phy_device *phydev, unsigned int modes); ++ ++ /** + * @get_rate_matching: Get the supported type of rate matching for a + * particular phy interface. This is used by phy consumers to determine + * whether to advertise lower-speed modes for that interface. It is +@@ -1870,6 +1875,7 @@ int phy_start_aneg(struct phy_device *ph + int phy_aneg_done(struct phy_device *phydev); + unsigned int phy_inband_caps(struct phy_device *phydev, + phy_interface_t interface); ++int phy_config_inband(struct phy_device *phydev, unsigned int modes); + int phy_speed_down(struct phy_device *phydev, bool sync); + int phy_speed_up(struct phy_device *phydev); + bool phy_check_valid(int speed, int duplex, unsigned long *features); diff --git a/target/linux/generic/backport-6.12/601-08-v6.14-net-phy-marvell-implement-config_inband-method.patch b/target/linux/generic/backport-6.12/601-08-v6.14-net-phy-marvell-implement-config_inband-method.patch new file mode 100644 index 00000000000..4531c507e26 --- /dev/null +++ b/target/linux/generic/backport-6.12/601-08-v6.14-net-phy-marvell-implement-config_inband-method.patch @@ -0,0 +1,77 @@ +From a219912e0fec73c346e64ef47013cb2e152f88fc Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:31:23 +0000 +Subject: [PATCH 08/13] net: phy: marvell: implement config_inband() method + +Implement the config_inband() method for Marvell 88E1112, 88E1111, +and Finisar's 88E1111 variant. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUrz-006IUO-3r@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/marvell.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -730,6 +730,34 @@ static unsigned int m88e1111_inband_caps + return 0; + } + ++static int m88e1111_config_inband(struct phy_device *phydev, unsigned int modes) ++{ ++ u16 extsr, bmcr; ++ int err; ++ ++ if (phydev->interface != PHY_INTERFACE_MODE_1000BASEX && ++ phydev->interface != PHY_INTERFACE_MODE_SGMII) ++ return -EINVAL; ++ ++ if (modes == LINK_INBAND_BYPASS) ++ extsr = MII_M1111_HWCFG_SERIAL_AN_BYPASS; ++ else ++ extsr = 0; ++ ++ if (modes == LINK_INBAND_DISABLE) ++ bmcr = 0; ++ else ++ bmcr = BMCR_ANENABLE; ++ ++ err = phy_modify(phydev, MII_M1111_PHY_EXT_SR, ++ MII_M1111_HWCFG_SERIAL_AN_BYPASS, extsr); ++ if (err < 0) ++ return extsr; ++ ++ return phy_modify_paged(phydev, MII_MARVELL_FIBER_PAGE, MII_BMCR, ++ BMCR_ANENABLE, bmcr); ++} ++ + static int m88e1111_config_aneg(struct phy_device *phydev) + { + int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR); +@@ -3719,6 +3747,7 @@ static struct phy_driver marvell_drivers + /* PHY_GBIT_FEATURES */ + .probe = marvell_probe, + .inband_caps = m88e1111_inband_caps, ++ .config_inband = m88e1111_config_inband, + .config_init = m88e1112_config_init, + .config_aneg = marvell_config_aneg, + .config_intr = marvell_config_intr, +@@ -3741,6 +3770,7 @@ static struct phy_driver marvell_drivers + .flags = PHY_POLL_CABLE_TEST, + .probe = marvell_probe, + .inband_caps = m88e1111_inband_caps, ++ .config_inband = m88e1111_config_inband, + .config_init = m88e1111gbe_config_init, + .config_aneg = m88e1111_config_aneg, + .read_status = marvell_read_status, +@@ -3765,6 +3795,7 @@ static struct phy_driver marvell_drivers + /* PHY_GBIT_FEATURES */ + .probe = marvell_probe, + .inband_caps = m88e1111_inband_caps, ++ .config_inband = m88e1111_config_inband, + .config_init = m88e1111gbe_config_init, + .config_aneg = m88e1111_config_aneg, + .read_status = marvell_read_status, diff --git a/target/linux/generic/backport-6.12/601-09-v6.14-net-phylink-add-pcs_inband_caps-method.patch b/target/linux/generic/backport-6.12/601-09-v6.14-net-phylink-add-pcs_inband_caps-method.patch new file mode 100644 index 00000000000..422802457e7 --- /dev/null +++ b/target/linux/generic/backport-6.12/601-09-v6.14-net-phylink-add-pcs_inband_caps-method.patch @@ -0,0 +1,159 @@ +From df874f9e52c340cc6f0a0014a97b778f67d46849 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:31:28 +0000 +Subject: [PATCH 09/13] net: phylink: add pcs_inband_caps() method + +Add a pcs_inband_caps() method to query the PCS for its inband link +capabilities, and use this to determine whether link modes used with +optical SFPs can be supported. + +When a PCS does not provide a method, we allow inband negotiation to +be either on or off, making this a no-op until the pcs_inband_caps() +method is implemented by a PCS driver. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUs4-006IUU-7K@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 60 +++++++++++++++++++++++++++++++++++++++ + include/linux/phylink.h | 17 +++++++++++ + 2 files changed, 77 insertions(+) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1007,6 +1007,15 @@ static void phylink_resolve_an_pause(str + } + } + ++static unsigned int phylink_pcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ if (pcs && pcs->ops->pcs_inband_caps) ++ return pcs->ops->pcs_inband_caps(pcs, interface); ++ ++ return 0; ++} ++ + static void phylink_pcs_pre_config(struct phylink_pcs *pcs, + phy_interface_t interface) + { +@@ -1060,6 +1069,24 @@ static void phylink_pcs_link_up(struct p + pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex); + } + ++/* Query inband for a specific interface mode, asking the MAC for the ++ * PCS which will be used to handle the interface mode. ++ */ ++static unsigned int phylink_inband_caps(struct phylink *pl, ++ phy_interface_t interface) ++{ ++ struct phylink_pcs *pcs; ++ ++ if (!pl->mac_ops->mac_select_pcs) ++ return 0; ++ ++ pcs = pl->mac_ops->mac_select_pcs(pl->config, interface); ++ if (!pcs) ++ return 0; ++ ++ return phylink_pcs_inband_caps(pcs, interface); ++} ++ + static void phylink_pcs_poll_stop(struct phylink *pl) + { + if (pl->cfg_link_an_mode == MLO_AN_INBAND) +@@ -2530,6 +2557,26 @@ int phylink_ethtool_ksettings_get(struct + } + EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get); + ++static bool phylink_validate_pcs_inband_autoneg(struct phylink *pl, ++ phy_interface_t interface, ++ unsigned long *adv) ++{ ++ unsigned int inband = phylink_inband_caps(pl, interface); ++ unsigned int mask; ++ ++ /* If the PCS doesn't implement inband support, be permissive. */ ++ if (!inband) ++ return true; ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv)) ++ mask = LINK_INBAND_ENABLE; ++ else ++ mask = LINK_INBAND_DISABLE; ++ ++ /* Check whether the PCS implements the required mode */ ++ return !!(inband & mask); ++} ++ + /** + * phylink_ethtool_ksettings_set() - set the link settings + * @pl: a pointer to a &struct phylink returned from phylink_create() +@@ -2665,6 +2712,13 @@ int phylink_ethtool_ksettings_set(struct + phylink_is_empty_linkmode(config.advertising)) + return -EINVAL; + ++ /* Validate the autonegotiation state. We don't have a PHY in this ++ * situation, so the PCS is the media-facing entity. ++ */ ++ if (!phylink_validate_pcs_inband_autoneg(pl, config.interface, ++ config.advertising)) ++ return -EINVAL; ++ + mutex_lock(&pl->state_mutex); + pl->link_config.speed = config.speed; + pl->link_config.duplex = config.duplex; +@@ -3349,6 +3403,12 @@ static int phylink_sfp_config_optical(st + phylink_dbg(pl, "optical SFP: chosen %s interface\n", + phy_modes(interface)); + ++ if (!phylink_validate_pcs_inband_autoneg(pl, interface, ++ config.advertising)) { ++ phylink_err(pl, "autoneg setting not compatible with PCS"); ++ return -EINVAL; ++ } ++ + config.interface = interface; + + /* Ignore errors if we're expecting a PHY to attach later */ +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -419,6 +419,7 @@ struct phylink_pcs { + /** + * struct phylink_pcs_ops - MAC PCS operations structure. + * @pcs_validate: validate the link configuration. ++ * @pcs_inband_caps: query inband support for interface mode. + * @pcs_enable: enable the PCS. + * @pcs_disable: disable the PCS. + * @pcs_pre_config: pre-mac_config method (for errata) +@@ -434,6 +435,8 @@ struct phylink_pcs { + struct phylink_pcs_ops { + int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported, + const struct phylink_link_state *state); ++ unsigned int (*pcs_inband_caps)(struct phylink_pcs *pcs, ++ phy_interface_t interface); + int (*pcs_enable)(struct phylink_pcs *pcs); + void (*pcs_disable)(struct phylink_pcs *pcs); + void (*pcs_pre_config)(struct phylink_pcs *pcs, +@@ -471,6 +474,20 @@ int pcs_validate(struct phylink_pcs *pcs + const struct phylink_link_state *state); + + /** ++ * pcs_inband_caps - query PCS in-band capabilities for interface mode. ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * @interface: interface mode to be queried ++ * ++ * Returns zero if it is unknown what in-band signalling is supported by the ++ * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise, ++ * returns a bit mask of the LINK_INBAND_* values from ++ * &enum link_inband_signalling to describe which inband modes are supported ++ * for this interface mode. ++ */ ++unsigned int pcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface); ++ ++/** + * pcs_enable() - enable the PCS. + * @pcs: a pointer to a &struct phylink_pcs. + */ diff --git a/target/linux/generic/backport-6.12/601-10-v6.14-net-mvneta-implement-pcs_inband_caps-method.patch b/target/linux/generic/backport-6.12/601-10-v6.14-net-mvneta-implement-pcs_inband_caps-method.patch new file mode 100644 index 00000000000..0937ad1388c --- /dev/null +++ b/target/linux/generic/backport-6.12/601-10-v6.14-net-mvneta-implement-pcs_inband_caps-method.patch @@ -0,0 +1,64 @@ +From 513e8fb8fa32035b3325e2e14fb9598f8cb545e9 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:31:33 +0000 +Subject: [PATCH 10/13] net: mvneta: implement pcs_inband_caps() method + +Report the PCS in-band capabilities to phylink for Marvell NETA +interfaces. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUs9-006IUb-Au@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/marvell/mvneta.c | 27 +++++++++++++++++---------- + 1 file changed, 17 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3960,20 +3960,27 @@ static struct mvneta_port *mvneta_pcs_to + return container_of(pcs, struct mvneta_port, phylink_pcs); + } + +-static int mvneta_pcs_validate(struct phylink_pcs *pcs, +- unsigned long *supported, +- const struct phylink_link_state *state) ++static unsigned int mvneta_pcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) + { +- /* We only support QSGMII, SGMII, 802.3z and RGMII modes. +- * When in 802.3z mode, we must have AN enabled: ++ /* When operating in an 802.3z mode, we must have AN enabled: + * "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... + * When = 1 (1000BASE-X) this field must be set to 1." ++ * Therefore, inband is "required". + */ +- if (phy_interface_mode_is_8023z(state->interface) && +- !phylink_test(state->advertising, Autoneg)) +- return -EINVAL; ++ if (phy_interface_mode_is_8023z(interface)) ++ return LINK_INBAND_ENABLE; + +- return 0; ++ /* QSGMII, SGMII and RGMII can be configured to use inband ++ * signalling of the AN result. Indicate these as "possible". ++ */ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ interface == PHY_INTERFACE_MODE_QSGMII || ++ phy_interface_mode_is_rgmii(interface)) ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; ++ ++ /* For any other modes, indicate that inband is not supported. */ ++ return LINK_INBAND_DISABLE; + } + + static void mvneta_pcs_get_state(struct phylink_pcs *pcs, +@@ -4071,7 +4078,7 @@ static void mvneta_pcs_an_restart(struct + } + + static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = { +- .pcs_validate = mvneta_pcs_validate, ++ .pcs_inband_caps = mvneta_pcs_inband_caps, + .pcs_get_state = mvneta_pcs_get_state, + .pcs_config = mvneta_pcs_config, + .pcs_an_restart = mvneta_pcs_an_restart, diff --git a/target/linux/generic/backport-6.12/601-11-v6.14-net-mvpp2-implement-pcs_inband_caps-method.patch b/target/linux/generic/backport-6.12/601-11-v6.14-net-mvpp2-implement-pcs_inband_caps-method.patch new file mode 100644 index 00000000000..846d9df36de --- /dev/null +++ b/target/linux/generic/backport-6.12/601-11-v6.14-net-mvpp2-implement-pcs_inband_caps-method.patch @@ -0,0 +1,62 @@ +From d4169f0c7665afb8d8adb5e1b1df3db88517d0ad Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:31:38 +0000 +Subject: [PATCH 11/13] net: mvpp2: implement pcs_inband_caps() method + +Report the PCS in-band capabilities to phylink for Marvell PP2 +interfaces. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUsE-006IUh-E7@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 25 ++++++++++++------- + 1 file changed, 16 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6237,19 +6237,26 @@ static const struct phylink_pcs_ops mvpp + .pcs_config = mvpp2_xlg_pcs_config, + }; + +-static int mvpp2_gmac_pcs_validate(struct phylink_pcs *pcs, +- unsigned long *supported, +- const struct phylink_link_state *state) ++static unsigned int mvpp2_gmac_pcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) + { +- /* When in 802.3z mode, we must have AN enabled: ++ /* When operating in an 802.3z mode, we must have AN enabled: + * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... + * When = 1 (1000BASE-X) this field must be set to 1. ++ * Therefore, inband is "required". + */ +- if (phy_interface_mode_is_8023z(state->interface) && +- !phylink_test(state->advertising, Autoneg)) +- return -EINVAL; ++ if (phy_interface_mode_is_8023z(interface)) ++ return LINK_INBAND_ENABLE; + +- return 0; ++ /* SGMII and RGMII can be configured to use inband signalling of the ++ * AN result. Indicate these as "possible". ++ */ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ phy_interface_mode_is_rgmii(interface)) ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; ++ ++ /* For any other modes, indicate that inband is not supported. */ ++ return LINK_INBAND_DISABLE; + } + + static void mvpp2_gmac_pcs_get_state(struct phylink_pcs *pcs, +@@ -6356,7 +6363,7 @@ static void mvpp2_gmac_pcs_an_restart(st + } + + static const struct phylink_pcs_ops mvpp2_phylink_gmac_pcs_ops = { +- .pcs_validate = mvpp2_gmac_pcs_validate, ++ .pcs_inband_caps = mvpp2_gmac_pcs_inband_caps, + .pcs_get_state = mvpp2_gmac_pcs_get_state, + .pcs_config = mvpp2_gmac_pcs_config, + .pcs_an_restart = mvpp2_gmac_pcs_an_restart, diff --git a/target/linux/generic/backport-6.12/601-12-v6.14-net-phylink-add-negotiation-of-in-band-capabilities.patch b/target/linux/generic/backport-6.12/601-12-v6.14-net-phylink-add-negotiation-of-in-band-capabilities.patch new file mode 100644 index 00000000000..f629133013b --- /dev/null +++ b/target/linux/generic/backport-6.12/601-12-v6.14-net-phylink-add-negotiation-of-in-band-capabilities.patch @@ -0,0 +1,228 @@ +From 5fd0f1a02e750e2db4038dee60edea669ce5aab1 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:31:43 +0000 +Subject: [PATCH 12/13] net: phylink: add negotiation of in-band capabilities + +Support for in-band signalling with Serdes links is uncertain. Some +PHYs do not support in-band for e.g. SGMII. Some PCS do not support +in-band for 2500Base-X. Some PCS require in-band for Base-X protocols. + +Simply using what is in DT is insufficient when we have hot-pluggable +PHYs e.g. in the form of SFP modules, which may not provide the +in-band signalling. + +In order to address this, we have introduced phy_inband_caps() and +pcs_inband_caps() functions to allow phylink to retrieve the +capabilities from each end of the PCS/PHY link. This commit adds code +to resolve whether in-band will be used in the various scenarios that +we have: In-band not being used, PHY present using SGMII or Base-X, +PHY not present. We also deal with no capabilties provided. + +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUsJ-006IUn-H3@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 154 +++++++++++++++++++++++++++++++++++--- + 1 file changed, 144 insertions(+), 10 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -75,6 +75,7 @@ struct phylink { + + struct mutex state_mutex; + struct phylink_link_state phy_state; ++ unsigned int phy_ib_mode; + struct work_struct resolve; + unsigned int pcs_neg_mode; + unsigned int pcs_state; +@@ -1170,10 +1171,18 @@ static void phylink_pcs_neg_mode(struct + phy_interface_t interface, + const unsigned long *advertising) + { ++ unsigned int pcs_ib_caps = 0; ++ unsigned int phy_ib_caps = 0; + unsigned int neg_mode, mode; ++ enum { ++ INBAND_CISCO_SGMII, ++ INBAND_BASEX, ++ } type; + + mode = pl->req_link_an_mode; + ++ pl->phy_ib_mode = 0; ++ + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: +@@ -1185,10 +1194,7 @@ static void phylink_pcs_neg_mode(struct + * inband communication. Note: there exist PHYs that run + * with SGMII but do not send the inband data. + */ +- if (!phylink_autoneg_inband(mode)) +- neg_mode = PHYLINK_PCS_NEG_OUTBAND; +- else +- neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; ++ type = INBAND_CISCO_SGMII; + break; + + case PHY_INTERFACE_MODE_1000BASEX: +@@ -1199,18 +1205,139 @@ static void phylink_pcs_neg_mode(struct + * as well, but drivers may not support this, so may + * need to override this. + */ +- if (!phylink_autoneg_inband(mode)) ++ type = INBAND_BASEX; ++ break; ++ ++ default: ++ pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE; ++ pl->act_link_an_mode = mode; ++ return; ++ } ++ ++ if (pcs) ++ pcs_ib_caps = phylink_pcs_inband_caps(pcs, interface); ++ ++ if (pl->phydev) ++ phy_ib_caps = phy_inband_caps(pl->phydev, interface); ++ ++ phylink_dbg(pl, "interface %s inband modes: pcs=%02x phy=%02x\n", ++ phy_modes(interface), pcs_ib_caps, phy_ib_caps); ++ ++ if (!phylink_autoneg_inband(mode)) { ++ bool pcs_ib_only = false; ++ bool phy_ib_only = false; ++ ++ if (pcs_ib_caps && pcs_ib_caps != LINK_INBAND_DISABLE) { ++ /* PCS supports reporting in-band capabilities, and ++ * supports more than disable mode. ++ */ ++ if (pcs_ib_caps & LINK_INBAND_DISABLE) ++ neg_mode = PHYLINK_PCS_NEG_OUTBAND; ++ else if (pcs_ib_caps & LINK_INBAND_ENABLE) ++ pcs_ib_only = true; ++ } ++ ++ if (phy_ib_caps && phy_ib_caps != LINK_INBAND_DISABLE) { ++ /* PHY supports in-band capabilities, and supports ++ * more than disable mode. ++ */ ++ if (phy_ib_caps & LINK_INBAND_DISABLE) ++ pl->phy_ib_mode = LINK_INBAND_DISABLE; ++ else if (phy_ib_caps & LINK_INBAND_BYPASS) ++ pl->phy_ib_mode = LINK_INBAND_BYPASS; ++ else if (phy_ib_caps & LINK_INBAND_ENABLE) ++ phy_ib_only = true; ++ } ++ ++ /* If either the PCS or PHY requires inband to be enabled, ++ * this is an invalid configuration. Provide a diagnostic ++ * message for this case, but don't try to force the issue. ++ */ ++ if (pcs_ib_only || phy_ib_only) ++ phylink_warn(pl, ++ "firmware wants %s mode, but %s%s%s requires inband\n", ++ phylink_an_mode_str(mode), ++ pcs_ib_only ? "PCS" : "", ++ pcs_ib_only && phy_ib_only ? " and " : "", ++ phy_ib_only ? "PHY" : ""); ++ ++ neg_mode = PHYLINK_PCS_NEG_OUTBAND; ++ } else if (type == INBAND_CISCO_SGMII || pl->phydev) { ++ /* For SGMII modes which are designed to be used with PHYs, or ++ * Base-X with a PHY, we try to use in-band mode where-ever ++ * possible. However, there are some PHYs e.g. BCM84881 which ++ * do not support in-band. ++ */ ++ const unsigned int inband_ok = LINK_INBAND_ENABLE | ++ LINK_INBAND_BYPASS; ++ const unsigned int outband_ok = LINK_INBAND_DISABLE | ++ LINK_INBAND_BYPASS; ++ /* PCS PHY ++ * D E D E ++ * 0 0 0 0 no information inband enabled ++ * 1 0 0 0 pcs doesn't support outband ++ * 0 1 0 0 pcs required inband enabled ++ * 1 1 0 0 pcs optional inband enabled ++ * 0 0 1 0 phy doesn't support outband ++ * 1 0 1 0 pcs+phy doesn't support outband ++ * 0 1 1 0 pcs required, phy doesn't support, invalid ++ * 1 1 1 0 pcs optional, phy doesn't support, outband ++ * 0 0 0 1 phy required inband enabled ++ * 1 0 0 1 pcs doesn't support, phy required, invalid ++ * 0 1 0 1 pcs+phy required inband enabled ++ * 1 1 0 1 pcs optional, phy required inband enabled ++ * 0 0 1 1 phy optional inband enabled ++ * 1 0 1 1 pcs doesn't support, phy optional, outband ++ * 0 1 1 1 pcs required, phy optional inband enabled ++ * 1 1 1 1 pcs+phy optional inband enabled ++ */ ++ if ((!pcs_ib_caps || pcs_ib_caps & inband_ok) && ++ (!phy_ib_caps || phy_ib_caps & inband_ok)) { ++ /* In-band supported or unknown at both ends. Enable ++ * in-band mode with or without bypass at the PHY. ++ */ ++ if (phy_ib_caps & LINK_INBAND_ENABLE) ++ pl->phy_ib_mode = LINK_INBAND_ENABLE; ++ else if (phy_ib_caps & LINK_INBAND_BYPASS) ++ pl->phy_ib_mode = LINK_INBAND_BYPASS; ++ ++ neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; ++ } else if ((!pcs_ib_caps || pcs_ib_caps & outband_ok) && ++ (!phy_ib_caps || phy_ib_caps & outband_ok)) { ++ /* Either in-band not supported at at least one end. ++ * In-band bypass at the other end is possible. ++ */ ++ if (phy_ib_caps & LINK_INBAND_DISABLE) ++ pl->phy_ib_mode = LINK_INBAND_DISABLE; ++ else if (phy_ib_caps & LINK_INBAND_BYPASS) ++ pl->phy_ib_mode = LINK_INBAND_BYPASS; ++ + neg_mode = PHYLINK_PCS_NEG_OUTBAND; ++ if (pl->phydev) ++ mode = MLO_AN_PHY; ++ } else { ++ /* invalid */ ++ phylink_warn(pl, "%s: incompatible in-band capabilities, trying in-band", ++ phy_modes(interface)); ++ neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; ++ } ++ } else { ++ /* For Base-X without a PHY */ ++ if (pcs_ib_caps == LINK_INBAND_DISABLE) ++ /* If the PCS doesn't support inband, then inband must ++ * be disabled. ++ */ ++ neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED; ++ else if (pcs_ib_caps == LINK_INBAND_ENABLE) ++ /* If the PCS requires inband, then inband must always ++ * be enabled. ++ */ ++ neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + advertising)) + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + else + neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED; +- break; +- +- default: +- neg_mode = PHYLINK_PCS_NEG_NONE; +- break; + } + + pl->pcs_neg_mode = neg_mode; +@@ -1309,6 +1436,13 @@ static void phylink_major_config(struct + ERR_PTR(err)); + } + ++ if (pl->phydev && pl->phy_ib_mode) { ++ err = phy_config_inband(pl->phydev, pl->phy_ib_mode); ++ if (err < 0) ++ phylink_err(pl, "phy_config_inband: %pe\n", ++ ERR_PTR(err)); ++ } ++ + if (pl->sfp_bus) { + rate_kbd = phylink_interface_signal_rate(state->interface); + if (rate_kbd) diff --git a/target/linux/generic/backport-6.12/601-13-v6.14-net-phylink-remove-phylink_phy_no_inband.patch b/target/linux/generic/backport-6.12/601-13-v6.14-net-phylink-remove-phylink_phy_no_inband.patch new file mode 100644 index 00000000000..8be74a2ad6b --- /dev/null +++ b/target/linux/generic/backport-6.12/601-13-v6.14-net-phylink-remove-phylink_phy_no_inband.patch @@ -0,0 +1,110 @@ +From 77ac9a8b2536e0eaca6c6f21070068458bf55981 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 3 Dec 2024 15:31:48 +0000 +Subject: [PATCH 13/13] net: phylink: remove phylink_phy_no_inband() + +Remove phylink_phy_no_inband() now that we are handling the lack of +inband negotiation by querying the capabilities of the PHY and PCS, +and the BCM84881 PHY driver provides us the information necessary to +make the decision. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tIUsO-006IUt-KN@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 27 ++++++--------------------- + 1 file changed, 6 insertions(+), 21 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -3394,10 +3394,11 @@ static phy_interface_t phylink_choose_sf + return interface; + } + +-static void phylink_sfp_set_config(struct phylink *pl, u8 mode, ++static void phylink_sfp_set_config(struct phylink *pl, + unsigned long *supported, + struct phylink_link_state *state) + { ++ u8 mode = MLO_AN_INBAND; + bool changed = false; + + phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n", +@@ -3431,8 +3432,7 @@ static void phylink_sfp_set_config(struc + phylink_mac_initial_config(pl, false); + } + +-static int phylink_sfp_config_phy(struct phylink *pl, u8 mode, +- struct phy_device *phy) ++static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy) + { + __ETHTOOL_DECLARE_LINK_MODE_MASK(support1); + __ETHTOOL_DECLARE_LINK_MODE_MASK(support); +@@ -3472,7 +3472,7 @@ static int phylink_sfp_config_phy(struct + if (ret) { + phylink_err(pl, + "validation of %s/%s with support %*pb failed: %pe\n", +- phylink_an_mode_str(mode), ++ phylink_an_mode_str(pl->req_link_an_mode), + phy_modes(config.interface), + __ETHTOOL_LINK_MODE_MASK_NBITS, support, + ERR_PTR(ret)); +@@ -3481,7 +3481,7 @@ static int phylink_sfp_config_phy(struct + + pl->link_port = pl->sfp_port; + +- phylink_sfp_set_config(pl, mode, support, &config); ++ phylink_sfp_set_config(pl, support, &config); + + return 0; + } +@@ -3556,7 +3556,7 @@ static int phylink_sfp_config_optical(st + + pl->link_port = pl->sfp_port; + +- phylink_sfp_set_config(pl, MLO_AN_INBAND, pl->sfp_support, &config); ++ phylink_sfp_set_config(pl, pl->sfp_support, &config); + + return 0; + } +@@ -3627,20 +3627,10 @@ static void phylink_sfp_link_up(void *up + phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_LINK); + } + +-/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII +- * or 802.3z control word, so inband will not work. +- */ +-static bool phylink_phy_no_inband(struct phy_device *phy) +-{ +- return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1], +- 0xae025150, 0xfffffff0); +-} +- + static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) + { + struct phylink *pl = upstream; + phy_interface_t interface; +- u8 mode; + int ret; + + /* +@@ -3652,17 +3642,12 @@ static int phylink_sfp_connect_phy(void + */ + phy_support_asym_pause(phy); + +- if (phylink_phy_no_inband(phy)) +- mode = MLO_AN_PHY; +- else +- mode = MLO_AN_INBAND; +- + /* Set the PHY's host supported interfaces */ + phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, + pl->config->supported_interfaces); + + /* Do the initial configuration */ +- ret = phylink_sfp_config_phy(pl, mode, phy); ++ ret = phylink_sfp_config_phy(pl, phy); + if (ret < 0) + return ret; + diff --git a/target/linux/generic/backport-6.12/602-01-v6.14-net-pcs-pcs-lynx-implement-pcs_inband_caps-method.patch b/target/linux/generic/backport-6.12/602-01-v6.14-net-pcs-pcs-lynx-implement-pcs_inband_caps-method.patch new file mode 100644 index 00000000000..32ba605b1c4 --- /dev/null +++ b/target/linux/generic/backport-6.12/602-01-v6.14-net-pcs-pcs-lynx-implement-pcs_inband_caps-method.patch @@ -0,0 +1,53 @@ +From 6561f0e547be221f411fda5eddfcc5bd8bb058a5 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 5 Dec 2024 09:42:24 +0000 +Subject: [PATCH 1/3] net: pcs: pcs-lynx: implement pcs_inband_caps() method + +Report the PCS in-band capabilities to phylink for the Lynx PCS. + +Reviewed-by: Maxime Chevallier +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tJ8NM-006L5J-AH@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/pcs/pcs-lynx.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +--- a/drivers/net/pcs/pcs-lynx.c ++++ b/drivers/net/pcs/pcs-lynx.c +@@ -35,6 +35,27 @@ enum sgmii_speed { + #define phylink_pcs_to_lynx(pl_pcs) container_of((pl_pcs), struct lynx_pcs, pcs) + #define lynx_to_phylink_pcs(lynx) (&(lynx)->pcs) + ++static unsigned int lynx_pcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; ++ ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return LINK_INBAND_DISABLE; ++ ++ case PHY_INTERFACE_MODE_USXGMII: ++ return LINK_INBAND_ENABLE; ++ ++ default: ++ return 0; ++ } ++} ++ + static void lynx_pcs_get_state_usxgmii(struct mdio_device *pcs, + struct phylink_link_state *state) + { +@@ -306,6 +327,7 @@ static void lynx_pcs_link_up(struct phyl + } + + static const struct phylink_pcs_ops lynx_pcs_phylink_ops = { ++ .pcs_inband_caps = lynx_pcs_inband_caps, + .pcs_get_state = lynx_pcs_get_state, + .pcs_config = lynx_pcs_config, + .pcs_an_restart = lynx_pcs_an_restart, diff --git a/target/linux/generic/backport-6.12/602-02-v6.14-net-pcs-pcs-mtk-lynxi-implement-pcs_inband_caps-meth.patch b/target/linux/generic/backport-6.12/602-02-v6.14-net-pcs-pcs-mtk-lynxi-implement-pcs_inband_caps-meth.patch new file mode 100644 index 00000000000..1cfd3bab9b6 --- /dev/null +++ b/target/linux/generic/backport-6.12/602-02-v6.14-net-pcs-pcs-mtk-lynxi-implement-pcs_inband_caps-meth.patch @@ -0,0 +1,47 @@ +From 520d29bdda86915b3caf8c72825a574bff212553 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 5 Dec 2024 09:42:29 +0000 +Subject: [PATCH 2/3] net: pcs: pcs-mtk-lynxi: implement pcs_inband_caps() + method + +Report the PCS in-band capabilities to phylink for the LynxI PCS. + +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tJ8NR-006L5P-E3@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/pcs/pcs-mtk-lynxi.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -88,6 +88,21 @@ static struct mtk_pcs_lynxi *pcs_to_mtk_ + return container_of(pcs, struct mtk_pcs_lynxi, pcs); + } + ++static unsigned int mtk_pcs_lynxi_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; ++ ++ default: ++ return 0; ++ } ++} ++ + static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) + { +@@ -241,6 +256,7 @@ static void mtk_pcs_lynxi_disable(struct + } + + static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = { ++ .pcs_inband_caps = mtk_pcs_lynxi_inband_caps, + .pcs_get_state = mtk_pcs_lynxi_get_state, + .pcs_config = mtk_pcs_lynxi_config, + .pcs_an_restart = mtk_pcs_lynxi_restart_an, diff --git a/target/linux/generic/backport-6.12/602-03-v6.14-net-pcs-xpcs-implement-pcs_inband_caps-method.patch b/target/linux/generic/backport-6.12/602-03-v6.14-net-pcs-xpcs-implement-pcs_inband_caps-method.patch new file mode 100644 index 00000000000..8dc495bd0c4 --- /dev/null +++ b/target/linux/generic/backport-6.12/602-03-v6.14-net-pcs-xpcs-implement-pcs_inband_caps-method.patch @@ -0,0 +1,58 @@ +From 484d0170d6c6bbb5213d037664e9a551f793bacd Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 5 Dec 2024 09:42:34 +0000 +Subject: [PATCH 3/3] net: pcs: xpcs: implement pcs_inband_caps() method + +Report the PCS inband capabilities to phylink for XPCS. + +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tJ8NW-006L5V-I9@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/pcs/pcs-xpcs.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +--- a/drivers/net/pcs/pcs-xpcs.c ++++ b/drivers/net/pcs/pcs-xpcs.c +@@ -608,6 +608,33 @@ static int xpcs_validate(struct phylink_ + return 0; + } + ++static unsigned int xpcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); ++ const struct dw_xpcs_compat *compat; ++ ++ compat = xpcs_find_compat(xpcs->desc, interface); ++ if (!compat) ++ return 0; ++ ++ switch (compat->an_mode) { ++ case DW_AN_C73: ++ return LINK_INBAND_ENABLE; ++ ++ case DW_AN_C37_SGMII: ++ case DW_AN_C37_1000BASEX: ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; ++ ++ case DW_10GBASER: ++ case DW_2500BASEX: ++ return LINK_INBAND_DISABLE; ++ ++ default: ++ return 0; ++ } ++} ++ + void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces) + { + int i, j; +@@ -1365,6 +1392,7 @@ static const struct dw_xpcs_desc xpcs_de + + static const struct phylink_pcs_ops xpcs_phylink_ops = { + .pcs_validate = xpcs_validate, ++ .pcs_inband_caps = xpcs_inband_caps, + .pcs_config = xpcs_config, + .pcs_get_state = xpcs_get_state, + .pcs_an_restart = xpcs_an_restart, diff --git a/target/linux/generic/backport-6.12/605-02-v6.17-net-phylink-provide-phylink_get_inband_type.patch b/target/linux/generic/backport-6.12/605-02-v6.17-net-phylink-provide-phylink_get_inband_type.patch new file mode 100644 index 00000000000..e944f573e09 --- /dev/null +++ b/target/linux/generic/backport-6.12/605-02-v6.17-net-phylink-provide-phylink_get_inband_type.patch @@ -0,0 +1,117 @@ +From 1bd905dfea9897eafef532000702e63a66849f54 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Sun, 31 Aug 2025 18:34:38 +0100 +Subject: net: phylink: provide phylink_get_inband_type() + +Provide a function to get the type of the inband signalling used for +a PHY interface type. This will be used in the subsequent patch to +address problems with 10G optical modules. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/E1uslws-00000001SP5-1R2R@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 79 ++++++++++++++++++++++----------------- + 1 file changed, 44 insertions(+), 35 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1147,6 +1147,42 @@ static void phylink_pcs_an_restart(struc + pl->pcs->ops->pcs_an_restart(pl->pcs); + } + ++enum inband_type { ++ INBAND_NONE, ++ INBAND_CISCO_SGMII, ++ INBAND_BASEX, ++}; ++ ++static enum inband_type phylink_get_inband_type(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_QUSGMII: ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10G_QXGMII: ++ /* These protocols are designed for use with a PHY which ++ * communicates its negotiation result back to the MAC via ++ * inband communication. Note: there exist PHYs that run ++ * with SGMII but do not send the inband data. ++ */ ++ return INBAND_CISCO_SGMII; ++ ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ /* 1000base-X is designed for use media-side for Fibre ++ * connections, and thus the Autoneg bit needs to be ++ * taken into account. We also do this for 2500base-X ++ * as well, but drivers may not support this, so may ++ * need to override this. ++ */ ++ return INBAND_BASEX; ++ ++ default: ++ return INBAND_NONE; ++ } ++} ++ + /** + * phylink_pcs_neg_mode() - helper to determine PCS inband mode + * @pl: a pointer to a &struct phylink returned from phylink_create() +@@ -1174,46 +1210,19 @@ static void phylink_pcs_neg_mode(struct + unsigned int pcs_ib_caps = 0; + unsigned int phy_ib_caps = 0; + unsigned int neg_mode, mode; +- enum { +- INBAND_CISCO_SGMII, +- INBAND_BASEX, +- } type; +- +- mode = pl->req_link_an_mode; ++ enum inband_type type; + +- pl->phy_ib_mode = 0; +- +- switch (interface) { +- case PHY_INTERFACE_MODE_SGMII: +- case PHY_INTERFACE_MODE_QSGMII: +- case PHY_INTERFACE_MODE_QUSGMII: +- case PHY_INTERFACE_MODE_USXGMII: +- case PHY_INTERFACE_MODE_10G_QXGMII: +- /* These protocols are designed for use with a PHY which +- * communicates its negotiation result back to the MAC via +- * inband communication. Note: there exist PHYs that run +- * with SGMII but do not send the inband data. +- */ +- type = INBAND_CISCO_SGMII; +- break; +- +- case PHY_INTERFACE_MODE_1000BASEX: +- case PHY_INTERFACE_MODE_2500BASEX: +- /* 1000base-X is designed for use media-side for Fibre +- * connections, and thus the Autoneg bit needs to be +- * taken into account. We also do this for 2500base-X +- * as well, but drivers may not support this, so may +- * need to override this. +- */ +- type = INBAND_BASEX; +- break; +- +- default: ++ type = phylink_get_inband_type(interface); ++ if (type == INBAND_NONE) { + pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE; +- pl->act_link_an_mode = mode; ++ pl->act_link_an_mode = pl->req_link_an_mode; + return; + } + ++ mode = pl->req_link_an_mode; ++ ++ pl->phy_ib_mode = 0; ++ + if (pcs) + pcs_ib_caps = phylink_pcs_inband_caps(pcs, interface); + diff --git a/target/linux/generic/backport-6.12/605-03-v6.17-net-phylink-disable-autoneg-for-interfaces-that-have.patch b/target/linux/generic/backport-6.12/605-03-v6.17-net-phylink-disable-autoneg-for-interfaces-that-have.patch new file mode 100644 index 00000000000..0861bb820de --- /dev/null +++ b/target/linux/generic/backport-6.12/605-03-v6.17-net-phylink-disable-autoneg-for-interfaces-that-have.patch @@ -0,0 +1,68 @@ +From a21202743f9ce4063e86b99cccaef48ef9813379 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Sun, 31 Aug 2025 18:34:43 +0100 +Subject: net: phylink: disable autoneg for interfaces that have no inband + +Mathew reports that as a result of commit 6561f0e547be ("net: pcs: +pcs-lynx: implement pcs_inband_caps() method"), 10G SFP modules no +longer work with the Lynx PCS. + +This problem is not specific to the Lynx PCS, but is caused by commit +df874f9e52c3 ("net: phylink: add pcs_inband_caps() method") which added +validation of the autoneg state to the optical SFP configuration path. + +Fix this by handling interface modes that fundamentally have no +inband negotiation more correctly - if we only have a single interface +mode, clear the Autoneg support bit and the advertising mask. If the +module can operate with several different interface modes, autoneg may +be supported for other modes, so leave the support mask alone and just +clear the Autoneg bit in the advertising mask. + +This restores 10G optical module functionality with PCS that supply +their inband support, and makes ethtool output look sane. + +Reported-by: Mathew McBride +Closes: https://lore.kernel.org/r/025c0ebe-5537-4fa3-b05a-8b835e5ad317@app.fastmail.com +Fixes: df874f9e52c3 ("net: phylink: add pcs_inband_caps() method") +Signed-off-by: Russell King (Oracle) +Tested-by: Vladimir Oltean +Link: https://patch.msgid.link/E1uslwx-00000001SPB-2kiM@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -3500,6 +3500,7 @@ static int phylink_sfp_config_optical(st + __ETHTOOL_DECLARE_LINK_MODE_MASK(support); + DECLARE_PHY_INTERFACE_MASK(interfaces); + struct phylink_link_state config; ++ enum inband_type inband_type; + phy_interface_t interface; + int ret; + +@@ -3546,6 +3547,23 @@ static int phylink_sfp_config_optical(st + phylink_dbg(pl, "optical SFP: chosen %s interface\n", + phy_modes(interface)); + ++ inband_type = phylink_get_inband_type(interface); ++ if (inband_type == INBAND_NONE) { ++ /* If this is the sole interface, and there is no inband ++ * support, clear the advertising mask and Autoneg bit in ++ * the support mask. Otherwise, just clear the Autoneg bit ++ * in the advertising mask. ++ */ ++ if (phy_interface_weight(pl->sfp_interfaces) == 1) { ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ pl->sfp_support); ++ linkmode_zero(config.advertising); ++ } else { ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ config.advertising); ++ } ++ } ++ + if (!phylink_validate_pcs_inband_autoneg(pl, interface, + config.advertising)) { + phylink_err(pl, "autoneg setting not compatible with PCS"); diff --git a/target/linux/generic/backport-6.12/610-01-v6.13-net-dsa-use-ethtool-string-helpers.patch b/target/linux/generic/backport-6.12/610-01-v6.13-net-dsa-use-ethtool-string-helpers.patch new file mode 100644 index 00000000000..a435e29144b --- /dev/null +++ b/target/linux/generic/backport-6.12/610-01-v6.13-net-dsa-use-ethtool-string-helpers.patch @@ -0,0 +1,30 @@ +From f12b363887c706c40611fba645265527a8415832 Mon Sep 17 00:00:00 2001 +From: Rosen Penev +Date: Sun, 27 Oct 2024 21:48:28 -0700 +Subject: [PATCH] net: dsa: use ethtool string helpers + +These are the preferred way to copy ethtool strings. + +Avoids incrementing pointers all over the place. + +Signed-off-by: Rosen Penev +(for hellcreek driver) +Reviewed-by: Kurt Kanzenbach +Link: https://patch.msgid.link/20241028044828.1639668-1-rosenp@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1086,8 +1086,7 @@ void b53_get_strings(struct dsa_switch * + + if (stringset == ETH_SS_STATS) { + for (i = 0; i < mib_size; i++) +- strscpy(data + i * ETH_GSTRING_LEN, +- mibs[i].name, ETH_GSTRING_LEN); ++ ethtool_puts(&data, mibs[i].name); + } else if (stringset == ETH_SS_PHY_STATS) { + phydev = b53_get_phy_device(ds, port); + if (!phydev) diff --git a/target/linux/generic/backport-6.12/610-02-v6.15-net-dsa-b53-mdio-add-support-for-BCM53101.patch b/target/linux/generic/backport-6.12/610-02-v6.15-net-dsa-b53-mdio-add-support-for-BCM53101.patch new file mode 100644 index 00000000000..6cbe547423b --- /dev/null +++ b/target/linux/generic/backport-6.12/610-02-v6.15-net-dsa-b53-mdio-add-support-for-BCM53101.patch @@ -0,0 +1,76 @@ +From c4f873c2b65c839ff5e7c996bd9ef5a1e7eae11a Mon Sep 17 00:00:00 2001 +From: Torben Nielsen +Date: Mon, 17 Feb 2025 09:05:01 +0100 +Subject: [PATCH] net: dsa: b53: mdio: add support for BCM53101 + +BCM53101 is a ethernet switch, very similar to the BCM53115. +Enable support for it, in the existing b53 dsa driver. + +Signed-off-by: Torben Nielsen +Signed-off-by: Claus Stovgaard +Link: https://patch.msgid.link/20250217080503.1390282-1-claus.stovgaard@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 14 ++++++++++++++ + drivers/net/dsa/b53/b53_mdio.c | 1 + + drivers/net/dsa/b53/b53_priv.h | 2 ++ + 3 files changed, 17 insertions(+) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2606,6 +2606,19 @@ static const struct b53_chip_data b53_sw + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, + }, + { ++ .chip_id = BCM53101_DEVICE_ID, ++ .dev_name = "BCM53101", ++ .vlans = 4096, ++ .enabled_ports = 0x11f, ++ .arl_bins = 4, ++ .arl_buckets = 512, ++ .vta_regs = B53_VTA_REGS, ++ .imp_port = 8, ++ .duplex_reg = B53_DUPLEX_STAT_GE, ++ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, ++ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ }, ++ { + .chip_id = BCM53115_DEVICE_ID, + .dev_name = "BCM53115", + .vlans = 4096, +@@ -2986,6 +2999,7 @@ int b53_switch_detect(struct b53_device + return ret; + + switch (id32) { ++ case BCM53101_DEVICE_ID: + case BCM53115_DEVICE_ID: + case BCM53125_DEVICE_ID: + case BCM53128_DEVICE_ID: +--- a/drivers/net/dsa/b53/b53_mdio.c ++++ b/drivers/net/dsa/b53/b53_mdio.c +@@ -374,6 +374,7 @@ static void b53_mdio_shutdown(struct mdi + + static const struct of_device_id b53_of_match[] = { + { .compatible = "brcm,bcm5325" }, ++ { .compatible = "brcm,bcm53101" }, + { .compatible = "brcm,bcm53115" }, + { .compatible = "brcm,bcm53125" }, + { .compatible = "brcm,bcm53128" }, +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -66,6 +66,7 @@ enum { + BCM5395_DEVICE_ID = 0x95, + BCM5397_DEVICE_ID = 0x97, + BCM5398_DEVICE_ID = 0x98, ++ BCM53101_DEVICE_ID = 0x53101, + BCM53115_DEVICE_ID = 0x53115, + BCM53125_DEVICE_ID = 0x53125, + BCM53128_DEVICE_ID = 0x53128, +@@ -190,6 +191,7 @@ static inline int is531x5(struct b53_dev + { + return dev->chip_id == BCM53115_DEVICE_ID || + dev->chip_id == BCM53125_DEVICE_ID || ++ dev->chip_id == BCM53101_DEVICE_ID || + dev->chip_id == BCM53128_DEVICE_ID || + dev->chip_id == BCM53134_DEVICE_ID; + } diff --git a/target/linux/generic/backport-6.12/610-03-v6.16-net-dsa-b53-implement-setting-ageing-time.patch b/target/linux/generic/backport-6.12/610-03-v6.16-net-dsa-b53-implement-setting-ageing-time.patch new file mode 100644 index 00000000000..196e70c42ce --- /dev/null +++ b/target/linux/generic/backport-6.12/610-03-v6.16-net-dsa-b53-implement-setting-ageing-time.patch @@ -0,0 +1,105 @@ +From e39d14a760c039af0653e3df967e7525413924a0 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Sat, 10 May 2025 11:22:11 +0200 +Subject: [PATCH] net: dsa: b53: implement setting ageing time + +b53 supported switches support configuring ageing time between 1 and +1,048,575 seconds, so add an appropriate setter. + +This allows b53 to pass the FDB learning test for both vlan aware and +vlan unaware bridges. + +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250510092211.276541-1-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 28 ++++++++++++++++++++++++++++ + drivers/net/dsa/b53/b53_priv.h | 1 + + drivers/net/dsa/b53/b53_regs.h | 7 +++++++ + 3 files changed, 36 insertions(+) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1227,6 +1228,10 @@ static int b53_setup(struct dsa_switch * + */ + ds->untag_vlan_aware_bridge_pvid = true; + ++ /* Ageing time is set in seconds */ ++ ds->ageing_time_min = 1 * 1000; ++ ds->ageing_time_max = AGE_TIME_MAX * 1000; ++ + ret = b53_reset_switch(dev); + if (ret) { + dev_err(ds->dev, "failed to reset switch\n"); +@@ -2466,6 +2471,28 @@ static int b53_get_max_mtu(struct dsa_sw + return B53_MAX_MTU; + } + ++int b53_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) ++{ ++ struct b53_device *dev = ds->priv; ++ u32 atc; ++ int reg; ++ ++ if (is63xx(dev)) ++ reg = B53_AGING_TIME_CONTROL_63XX; ++ else ++ reg = B53_AGING_TIME_CONTROL; ++ ++ atc = DIV_ROUND_CLOSEST(msecs, 1000); ++ ++ if (!is5325(dev) && !is5365(dev)) ++ atc |= AGE_CHANGE; ++ ++ b53_write32(dev, B53_MGMT_PAGE, reg, atc); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(b53_set_ageing_time); ++ + static const struct phylink_mac_ops b53_phylink_mac_ops = { + .mac_select_pcs = b53_phylink_mac_select_pcs, + .mac_config = b53_phylink_mac_config, +@@ -2490,6 +2517,7 @@ static const struct dsa_switch_ops b53_s + .support_eee = b53_support_eee, + .get_mac_eee = b53_get_mac_eee, + .set_mac_eee = b53_set_mac_eee, ++ .set_ageing_time = b53_set_ageing_time, + .port_bridge_join = b53_br_join, + .port_bridge_leave = b53_br_leave, + .port_pre_bridge_flags = b53_br_flags_pre, +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -343,6 +343,7 @@ void b53_get_strings(struct dsa_switch * + void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); + int b53_get_sset_count(struct dsa_switch *ds, int port, int sset); + void b53_get_ethtool_phy_stats(struct dsa_switch *ds, int port, uint64_t *data); ++int b53_set_ageing_time(struct dsa_switch *ds, unsigned int msecs); + int b53_br_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, + bool *tx_fwd_offload, struct netlink_ext_ack *extack); + void b53_br_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge); +--- a/drivers/net/dsa/b53/b53_regs.h ++++ b/drivers/net/dsa/b53/b53_regs.h +@@ -224,6 +224,13 @@ + #define BRCM_HDR_P5_EN BIT(1) /* Enable tagging on port 5 */ + #define BRCM_HDR_P7_EN BIT(2) /* Enable tagging on port 7 */ + ++/* Aging Time control register (32 bit) */ ++#define B53_AGING_TIME_CONTROL 0x06 ++#define B53_AGING_TIME_CONTROL_63XX 0x08 ++#define AGE_CHANGE BIT(20) ++#define AGE_TIME_MASK 0x7ffff ++#define AGE_TIME_MAX 1048575 ++ + /* Mirror capture control register (16 bit) */ + #define B53_MIR_CAP_CTL 0x10 + #define CAP_PORT_MASK 0xf diff --git a/target/linux/generic/backport-6.12/610-04-v6.16-net-dsa-b53-do-not-configure-bcm63xx-s-IMP-port-inte.patch b/target/linux/generic/backport-6.12/610-04-v6.16-net-dsa-b53-do-not-configure-bcm63xx-s-IMP-port-inte.patch new file mode 100644 index 00000000000..e70f09e0509 --- /dev/null +++ b/target/linux/generic/backport-6.12/610-04-v6.16-net-dsa-b53-do-not-configure-bcm63xx-s-IMP-port-inte.patch @@ -0,0 +1,70 @@ +From 75f4f7b2b13008803f84768ff90396f9d7553221 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Mon, 2 Jun 2025 21:39:51 +0200 +Subject: [PATCH] net: dsa: b53: do not configure bcm63xx's IMP port interface + +The IMP port is not a valid RGMII interface, but hard wired to internal, +so we shouldn't touch the undefined register B53_RGMII_CTRL_IMP. + +While this does not seem to have any side effects, let's not touch it at +all, so limit RGMII configuration on bcm63xx to the actual RGMII ports. + +Fixes: ce3bf94871f7 ("net: dsa: b53: add support for BCM63xx RGMIIs") +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250602193953.1010487-4-jonas.gorski@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/b53/b53_common.c | 22 ++++++++-------------- + 1 file changed, 8 insertions(+), 14 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1369,24 +1370,17 @@ static void b53_adjust_63xx_rgmii(struct + phy_interface_t interface) + { + struct b53_device *dev = ds->priv; +- u8 rgmii_ctrl = 0, off; +- +- if (port == dev->imp_port) +- off = B53_RGMII_CTRL_IMP; +- else +- off = B53_RGMII_CTRL_P(port); ++ u8 rgmii_ctrl = 0; + +- b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl); ++ b53_read8(dev, B53_CTRL_PAGE, B53_RGMII_CTRL_P(port), &rgmii_ctrl); + rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); + +- if (port != dev->imp_port) { +- if (is63268(dev)) +- rgmii_ctrl |= RGMII_CTRL_MII_OVERRIDE; ++ if (is63268(dev)) ++ rgmii_ctrl |= RGMII_CTRL_MII_OVERRIDE; + +- rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII; +- } ++ rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII; + +- b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl); ++ b53_write8(dev, B53_CTRL_PAGE, B53_RGMII_CTRL_P(port), rgmii_ctrl); + + dev_dbg(ds->dev, "Configured port %d for %s\n", port, + phy_modes(interface)); +@@ -1537,7 +1531,7 @@ static void b53_phylink_mac_config(struc + struct b53_device *dev = ds->priv; + int port = dp->index; + +- if (is63xx(dev) && port >= B53_63XX_RGMII0) ++ if (is63xx(dev) && in_range(port, B53_63XX_RGMII0, 4)) + b53_adjust_63xx_rgmii(ds, port, interface); + + if (mode == MLO_AN_FIXED) { diff --git a/target/linux/generic/backport-6.12/611-01-v6.17-net-dsa-tag_brcm-add-support-for-legacy-FCS-tags.patch b/target/linux/generic/backport-6.12/611-01-v6.17-net-dsa-tag_brcm-add-support-for-legacy-FCS-tags.patch new file mode 100644 index 00000000000..88f10be377b --- /dev/null +++ b/target/linux/generic/backport-6.12/611-01-v6.17-net-dsa-tag_brcm-add-support-for-legacy-FCS-tags.patch @@ -0,0 +1,188 @@ +From ef07df397a621707903ef0d294a7df11f80cf206 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Sat, 14 Jun 2025 09:59:48 +0200 +Subject: [PATCH] net: dsa: tag_brcm: add support for legacy FCS tags +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for legacy Broadcom FCS tags, which are similar to +DSA_TAG_PROTO_BRCM_LEGACY. +BCM5325 and BCM5365 switches require including the original FCS value and +length, as opposed to BCM63xx switches. +Adding the original FCS value and length to DSA_TAG_PROTO_BRCM_LEGACY would +impact performance of BCM63xx switches, so it's better to create a new tag. + +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250614080000.1884236-3-noltari@gmail.com +Signed-off-by: Jakub Kicinski +--- + include/net/dsa.h | 2 ++ + net/dsa/Kconfig | 16 ++++++++-- + net/dsa/tag_brcm.c | 73 +++++++++++++++++++++++++++++++++++++++++++++- + 3 files changed, 88 insertions(+), 3 deletions(-) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -54,11 +54,13 @@ struct tc_action; + #define DSA_TAG_PROTO_RZN1_A5PSW_VALUE 26 + #define DSA_TAG_PROTO_LAN937X_VALUE 27 + #define DSA_TAG_PROTO_VSC73XX_8021Q_VALUE 28 ++#define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29 + + enum dsa_tag_protocol { + DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, + DSA_TAG_PROTO_BRCM = DSA_TAG_PROTO_BRCM_VALUE, + DSA_TAG_PROTO_BRCM_LEGACY = DSA_TAG_PROTO_BRCM_LEGACY_VALUE, ++ DSA_TAG_PROTO_BRCM_LEGACY_FCS = DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE, + DSA_TAG_PROTO_BRCM_PREPEND = DSA_TAG_PROTO_BRCM_PREPEND_VALUE, + DSA_TAG_PROTO_DSA = DSA_TAG_PROTO_DSA_VALUE, + DSA_TAG_PROTO_EDSA = DSA_TAG_PROTO_EDSA_VALUE, +--- a/net/dsa/Kconfig ++++ b/net/dsa/Kconfig +@@ -42,12 +42,24 @@ config NET_DSA_TAG_BRCM + Broadcom switches which place the tag after the MAC source address. + + config NET_DSA_TAG_BRCM_LEGACY +- tristate "Tag driver for Broadcom legacy switches using in-frame headers" ++ tristate "Tag driver for BCM63xx legacy switches using in-frame headers" + select NET_DSA_TAG_BRCM_COMMON + help + Say Y if you want to enable support for tagging frames for the +- Broadcom legacy switches which place the tag after the MAC source ++ BCM63xx legacy switches which place the tag after the MAC source + address. ++ This tag is used in BCM63xx legacy switches which work without the ++ original FCS and length before the tag insertion. ++ ++config NET_DSA_TAG_BRCM_LEGACY_FCS ++ tristate "Tag driver for BCM53xx legacy switches using in-frame headers" ++ select NET_DSA_TAG_BRCM_COMMON ++ help ++ Say Y if you want to enable support for tagging frames for the ++ BCM53xx legacy switches which place the tag after the MAC source ++ address. ++ This tag is used in BCM53xx legacy switches which expect original ++ FCS and length before the tag insertion to be present. + + config NET_DSA_TAG_BRCM_PREPEND + tristate "Tag driver for Broadcom switches using prepended headers" +--- a/net/dsa/tag_brcm.c ++++ b/net/dsa/tag_brcm.c +@@ -15,6 +15,7 @@ + + #define BRCM_NAME "brcm" + #define BRCM_LEGACY_NAME "brcm-legacy" ++#define BRCM_LEGACY_FCS_NAME "brcm-legacy-fcs" + #define BRCM_PREPEND_NAME "brcm-prepend" + + /* Legacy Broadcom tag (6 bytes) */ +@@ -32,6 +33,10 @@ + #define BRCM_LEG_MULTICAST (1 << 5) + #define BRCM_LEG_EGRESS (2 << 5) + #define BRCM_LEG_INGRESS (3 << 5) ++#define BRCM_LEG_LEN_HI(x) (((x) >> 8) & 0x7) ++ ++/* 4th byte in the tag */ ++#define BRCM_LEG_LEN_LO(x) ((x) & 0xff) + + /* 6th byte in the tag */ + #define BRCM_LEG_PORT_ID (0xf) +@@ -212,7 +217,8 @@ DSA_TAG_DRIVER(brcm_netdev_ops); + MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM, BRCM_NAME); + #endif + +-#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) ++#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) || \ ++ IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS) + static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, + struct net_device *dev) + { +@@ -250,7 +256,9 @@ static struct sk_buff *brcm_leg_tag_rcv( + + return skb; + } ++#endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY || CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS */ + ++#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) + static struct sk_buff *brcm_leg_tag_xmit(struct sk_buff *skb, + struct net_device *dev) + { +@@ -300,6 +308,66 @@ DSA_TAG_DRIVER(brcm_legacy_netdev_ops); + MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY, BRCM_LEGACY_NAME); + #endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY */ + ++#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS) ++static struct sk_buff *brcm_leg_fcs_tag_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct dsa_port *dp = dsa_user_to_port(dev); ++ unsigned int fcs_len; ++ __le32 fcs_val; ++ u8 *brcm_tag; ++ ++ /* The Ethernet switch we are interfaced with needs packets to be at ++ * least 64 bytes (including FCS) otherwise they will be discarded when ++ * they enter the switch port logic. When Broadcom tags are enabled, we ++ * need to make sure that packets are at least 70 bytes (including FCS ++ * and tag) because the length verification is done after the Broadcom ++ * tag is stripped off the ingress packet. ++ * ++ * Let dsa_user_xmit() free the SKB. ++ */ ++ if (__skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN, false)) ++ return NULL; ++ ++ fcs_len = skb->len; ++ fcs_val = cpu_to_le32(crc32_le(~0, skb->data, fcs_len) ^ ~0); ++ ++ skb_push(skb, BRCM_LEG_TAG_LEN); ++ ++ dsa_alloc_etype_header(skb, BRCM_LEG_TAG_LEN); ++ ++ brcm_tag = skb->data + 2 * ETH_ALEN; ++ ++ /* Broadcom tag type */ ++ brcm_tag[0] = BRCM_LEG_TYPE_HI; ++ brcm_tag[1] = BRCM_LEG_TYPE_LO; ++ ++ /* Broadcom tag value */ ++ brcm_tag[2] = BRCM_LEG_EGRESS | BRCM_LEG_LEN_HI(fcs_len); ++ brcm_tag[3] = BRCM_LEG_LEN_LO(fcs_len); ++ brcm_tag[4] = 0; ++ brcm_tag[5] = dp->index & BRCM_LEG_PORT_ID; ++ ++ /* Original FCS value */ ++ if (__skb_pad(skb, ETH_FCS_LEN, false)) ++ return NULL; ++ skb_put_data(skb, &fcs_val, ETH_FCS_LEN); ++ ++ return skb; ++} ++ ++static const struct dsa_device_ops brcm_legacy_fcs_netdev_ops = { ++ .name = BRCM_LEGACY_FCS_NAME, ++ .proto = DSA_TAG_PROTO_BRCM_LEGACY_FCS, ++ .xmit = brcm_leg_fcs_tag_xmit, ++ .rcv = brcm_leg_tag_rcv, ++ .needed_headroom = BRCM_LEG_TAG_LEN, ++}; ++ ++DSA_TAG_DRIVER(brcm_legacy_fcs_netdev_ops); ++MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY_FCS, BRCM_LEGACY_FCS_NAME); ++#endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS */ ++ + #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND) + static struct sk_buff *brcm_tag_xmit_prepend(struct sk_buff *skb, + struct net_device *dev) +@@ -334,6 +402,9 @@ static struct dsa_tag_driver *dsa_tag_dr + #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) + &DSA_TAG_DRIVER_NAME(brcm_legacy_netdev_ops), + #endif ++#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS) ++ &DSA_TAG_DRIVER_NAME(brcm_legacy_fcs_netdev_ops), ++#endif + #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND) + &DSA_TAG_DRIVER_NAME(brcm_prepend_netdev_ops), + #endif diff --git a/target/linux/generic/backport-6.12/611-02-v6.17-net-dsa-b53-support-legacy-FCS-tags.patch b/target/linux/generic/backport-6.12/611-02-v6.17-net-dsa-b53-support-legacy-FCS-tags.patch new file mode 100644 index 00000000000..b04c8ea27ed --- /dev/null +++ b/target/linux/generic/backport-6.12/611-02-v6.17-net-dsa-b53-support-legacy-FCS-tags.patch @@ -0,0 +1,48 @@ +From c3cf059a4d419b9c888ce7e9952fa13ba7569b61 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Sat, 14 Jun 2025 09:59:49 +0200 +Subject: [PATCH] net: dsa: b53: support legacy FCS tags +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit 46c5176c586c ("net: dsa: b53: support legacy tags") introduced +support for legacy tags, but it turns out that BCM5325 and BCM5365 +switches require the original FCS value and length, so they have to be +treated differently. + +Reviewed-by: Florian Fainelli +Signed-off-by: Álvaro Fernández Rojas +Link: https://patch.msgid.link/20250614080000.1884236-4-noltari@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/Kconfig | 1 + + drivers/net/dsa/b53/b53_common.c | 7 +++++-- + 2 files changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/b53/Kconfig ++++ b/drivers/net/dsa/b53/Kconfig +@@ -5,6 +5,7 @@ menuconfig B53 + select NET_DSA_TAG_NONE + select NET_DSA_TAG_BRCM + select NET_DSA_TAG_BRCM_LEGACY ++ select NET_DSA_TAG_BRCM_LEGACY_FCS + select NET_DSA_TAG_BRCM_PREPEND + help + This driver adds support for Broadcom managed switch chips. It supports +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2307,8 +2307,11 @@ enum dsa_tag_protocol b53_get_tag_protoc + goto out; + } + +- /* Older models require a different 6 byte tag */ +- if (is5325(dev) || is5365(dev) || is63xx(dev)) { ++ /* Older models require different 6 byte tags */ ++ if (is5325(dev) || is5365(dev)) { ++ dev->tag_protocol = DSA_TAG_PROTO_BRCM_LEGACY_FCS; ++ goto out; ++ } else if (is63xx(dev)) { + dev->tag_protocol = DSA_TAG_PROTO_BRCM_LEGACY; + goto out; + } diff --git a/target/linux/generic/backport-6.12/611-03-v6.17-net-dsa-b53-detect-BCM5325-variants.patch b/target/linux/generic/backport-6.12/611-03-v6.17-net-dsa-b53-detect-BCM5325-variants.patch new file mode 100644 index 00000000000..3c2fa2d8c44 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-03-v6.17-net-dsa-b53-detect-BCM5325-variants.patch @@ -0,0 +1,112 @@ +From 0cbec9aef5a86194117a956546dc1aec95031f37 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Sat, 14 Jun 2025 09:59:50 +0200 +Subject: [PATCH] net: dsa: b53: detect BCM5325 variants +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We need to be able to differentiate the BCM5325 variants because: +- BCM5325M switches lack the ARLIO_PAGE->VLAN_ID_IDX register. +- BCM5325E have less 512 ARL buckets instead of 1024. + +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250614080000.1884236-5-noltari@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 24 +++++++++++++++++++++--- + drivers/net/dsa/b53/b53_priv.h | 19 +++++++++++++++++++ + 2 files changed, 40 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1835,7 +1835,8 @@ static int b53_arl_op(struct b53_device + + /* Perform a read for the given MAC and VID */ + b53_write48(dev, B53_ARLIO_PAGE, B53_MAC_ADDR_IDX, mac); +- b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid); ++ if (!is5325m(dev)) ++ b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid); + + /* Issue a read operation for this MAC */ + ret = b53_arl_rw_op(dev, 1); +@@ -2906,6 +2907,9 @@ static int b53_switch_init(struct b53_de + } + } + ++ if (is5325e(dev)) ++ dev->num_arl_buckets = 512; ++ + dev->num_ports = fls(dev->enabled_ports); + + dev->ds->num_ports = min_t(unsigned int, dev->num_ports, DSA_MAX_PORTS); +@@ -3007,10 +3011,24 @@ int b53_switch_detect(struct b53_device + b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf); + b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp); + +- if (tmp == 0xf) ++ if (tmp == 0xf) { ++ u32 phy_id; ++ int val; ++ + dev->chip_id = BCM5325_DEVICE_ID; +- else ++ ++ val = b53_phy_read16(dev->ds, 0, MII_PHYSID1); ++ phy_id = (val & 0xffff) << 16; ++ val = b53_phy_read16(dev->ds, 0, MII_PHYSID2); ++ phy_id |= (val & 0xfff0); ++ ++ if (phy_id == 0x00406330) ++ dev->variant_id = B53_VARIANT_5325M; ++ else if (phy_id == 0x0143bc30) ++ dev->variant_id = B53_VARIANT_5325E; ++ } else { + dev->chip_id = BCM5365_DEVICE_ID; ++ } + break; + case BCM5389_DEVICE_ID: + case BCM5395_DEVICE_ID: +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -84,6 +84,12 @@ enum { + BCM53134_DEVICE_ID = 0x5075, + }; + ++enum b53_variant_id { ++ B53_VARIANT_NONE = 0, ++ B53_VARIANT_5325E, ++ B53_VARIANT_5325M, ++}; ++ + struct b53_pcs { + struct phylink_pcs pcs; + struct b53_device *dev; +@@ -118,6 +124,7 @@ struct b53_device { + + /* chip specific data */ + u32 chip_id; ++ enum b53_variant_id variant_id; + u8 core_rev; + u8 vta_regs[3]; + u8 duplex_reg; +@@ -165,6 +172,18 @@ static inline int is5325(struct b53_devi + return dev->chip_id == BCM5325_DEVICE_ID; + } + ++static inline int is5325e(struct b53_device *dev) ++{ ++ return is5325(dev) && ++ dev->variant_id == B53_VARIANT_5325E; ++} ++ ++static inline int is5325m(struct b53_device *dev) ++{ ++ return is5325(dev) && ++ dev->variant_id == B53_VARIANT_5325M; ++} ++ + static inline int is5365(struct b53_device *dev) + { + #ifdef CONFIG_BCM47XX diff --git a/target/linux/generic/backport-6.12/611-04-v6.17-net-dsa-b53-add-support-for-FDB-operations-on-5325-5365.patch b/target/linux/generic/backport-6.12/611-04-v6.17-net-dsa-b53-add-support-for-FDB-operations-on-5325-5365.patch new file mode 100644 index 00000000000..9de73815daa --- /dev/null +++ b/target/linux/generic/backport-6.12/611-04-v6.17-net-dsa-b53-add-support-for-FDB-operations-on-5325-5365.patch @@ -0,0 +1,250 @@ +From c45655386e532c85ff1d679fc2aa40b3aaff9916 Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +Date: Sat, 14 Jun 2025 09:59:51 +0200 +Subject: [PATCH] net: dsa: b53: add support for FDB operations on 5325/5365 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BCM5325 and BCM5365 are part of a much older generation of switches which, +due to their limited number of ports and VLAN entries (up to 256) allowed +a single 64-bit register to hold a full ARL entry. +This requires a little bit of massaging when reading, writing and +converting ARL entries in both directions. + +Signed-off-by: Florian Fainelli +Signed-off-by: Álvaro Fernández Rojas +Link: https://patch.msgid.link/20250614080000.1884236-6-noltari@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 101 +++++++++++++++++++++++++------ + drivers/net/dsa/b53/b53_priv.h | 29 +++++++++ + drivers/net/dsa/b53/b53_regs.h | 7 ++- + 3 files changed, 115 insertions(+), 22 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1821,6 +1821,45 @@ static int b53_arl_read(struct b53_devic + return *idx >= dev->num_arl_bins ? -ENOSPC : -ENOENT; + } + ++static int b53_arl_read_25(struct b53_device *dev, u64 mac, ++ u16 vid, struct b53_arl_entry *ent, u8 *idx) ++{ ++ DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES); ++ unsigned int i; ++ int ret; ++ ++ ret = b53_arl_op_wait(dev); ++ if (ret) ++ return ret; ++ ++ bitmap_zero(free_bins, dev->num_arl_bins); ++ ++ /* Read the bins */ ++ for (i = 0; i < dev->num_arl_bins; i++) { ++ u64 mac_vid; ++ ++ b53_read64(dev, B53_ARLIO_PAGE, ++ B53_ARLTBL_MAC_VID_ENTRY(i), &mac_vid); ++ ++ b53_arl_to_entry_25(ent, mac_vid); ++ ++ if (!(mac_vid & ARLTBL_VALID_25)) { ++ set_bit(i, free_bins); ++ continue; ++ } ++ if ((mac_vid & ARLTBL_MAC_MASK) != mac) ++ continue; ++ if (dev->vlan_enabled && ++ ((mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25) != vid) ++ continue; ++ *idx = i; ++ return 0; ++ } ++ ++ *idx = find_first_bit(free_bins, dev->num_arl_bins); ++ return *idx >= dev->num_arl_bins ? -ENOSPC : -ENOENT; ++} ++ + static int b53_arl_op(struct b53_device *dev, int op, int port, + const unsigned char *addr, u16 vid, bool is_valid) + { +@@ -1843,7 +1882,10 @@ static int b53_arl_op(struct b53_device + if (ret) + return ret; + +- ret = b53_arl_read(dev, mac, vid, &ent, &idx); ++ if (is5325(dev) || is5365(dev)) ++ ret = b53_arl_read_25(dev, mac, vid, &ent, &idx); ++ else ++ ret = b53_arl_read(dev, mac, vid, &ent, &idx); + + /* If this is a read, just finish now */ + if (op) +@@ -1887,12 +1929,17 @@ static int b53_arl_op(struct b53_device + ent.is_static = true; + ent.is_age = false; + memcpy(ent.mac, addr, ETH_ALEN); +- b53_arl_from_entry(&mac_vid, &fwd_entry, &ent); ++ if (is5325(dev) || is5365(dev)) ++ b53_arl_from_entry_25(&mac_vid, &ent); ++ else ++ b53_arl_from_entry(&mac_vid, &fwd_entry, &ent); + + b53_write64(dev, B53_ARLIO_PAGE, + B53_ARLTBL_MAC_VID_ENTRY(idx), mac_vid); +- b53_write32(dev, B53_ARLIO_PAGE, +- B53_ARLTBL_DATA_ENTRY(idx), fwd_entry); ++ ++ if (!is5325(dev) && !is5365(dev)) ++ b53_write32(dev, B53_ARLIO_PAGE, ++ B53_ARLTBL_DATA_ENTRY(idx), fwd_entry); + + return b53_arl_rw_op(dev, 0); + } +@@ -1904,12 +1951,6 @@ int b53_fdb_add(struct dsa_switch *ds, i + struct b53_device *priv = ds->priv; + int ret; + +- /* 5325 and 5365 require some more massaging, but could +- * be supported eventually +- */ +- if (is5325(priv) || is5365(priv)) +- return -EOPNOTSUPP; +- + mutex_lock(&priv->arl_mutex); + ret = b53_arl_op(priv, 0, port, addr, vid, true); + mutex_unlock(&priv->arl_mutex); +@@ -1936,10 +1977,15 @@ EXPORT_SYMBOL(b53_fdb_del); + static int b53_arl_search_wait(struct b53_device *dev) + { + unsigned int timeout = 1000; +- u8 reg; ++ u8 reg, offset; ++ ++ if (is5325(dev) || is5365(dev)) ++ offset = B53_ARL_SRCH_CTL_25; ++ else ++ offset = B53_ARL_SRCH_CTL; + + do { +- b53_read8(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, ®); ++ b53_read8(dev, B53_ARLIO_PAGE, offset, ®); + if (!(reg & ARL_SRCH_STDN)) + return -ENOENT; + +@@ -1956,13 +2002,24 @@ static void b53_arl_search_rd(struct b53 + struct b53_arl_entry *ent) + { + u64 mac_vid; +- u32 fwd_entry; + +- b53_read64(dev, B53_ARLIO_PAGE, +- B53_ARL_SRCH_RSTL_MACVID(idx), &mac_vid); +- b53_read32(dev, B53_ARLIO_PAGE, +- B53_ARL_SRCH_RSTL(idx), &fwd_entry); +- b53_arl_to_entry(ent, mac_vid, fwd_entry); ++ if (is5325(dev)) { ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_25, ++ &mac_vid); ++ b53_arl_to_entry_25(ent, mac_vid); ++ } else if (is5365(dev)) { ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_65, ++ &mac_vid); ++ b53_arl_to_entry_25(ent, mac_vid); ++ } else { ++ u32 fwd_entry; ++ ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_MACVID(idx), ++ &mac_vid); ++ b53_read32(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL(idx), ++ &fwd_entry); ++ b53_arl_to_entry(ent, mac_vid, fwd_entry); ++ } + } + + static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, +@@ -1986,14 +2043,20 @@ int b53_fdb_dump(struct dsa_switch *ds, + struct b53_device *priv = ds->priv; + struct b53_arl_entry results[2]; + unsigned int count = 0; ++ u8 offset; + int ret; + u8 reg; + + mutex_lock(&priv->arl_mutex); + ++ if (is5325(priv) || is5365(priv)) ++ offset = B53_ARL_SRCH_CTL_25; ++ else ++ offset = B53_ARL_SRCH_CTL; ++ + /* Start search operation */ + reg = ARL_SRCH_STDN; +- b53_write8(priv, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, reg); ++ b53_write8(priv, offset, B53_ARL_SRCH_CTL, reg); + + do { + ret = b53_arl_search_wait(priv); +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -317,6 +317,19 @@ static inline void b53_arl_to_entry(stru + ent->vid = mac_vid >> ARLTBL_VID_S; + } + ++static inline void b53_arl_to_entry_25(struct b53_arl_entry *ent, ++ u64 mac_vid) ++{ ++ memset(ent, 0, sizeof(*ent)); ++ ent->port = (mac_vid >> ARLTBL_DATA_PORT_ID_S_25) & ++ ARLTBL_DATA_PORT_ID_MASK_25; ++ ent->is_valid = !!(mac_vid & ARLTBL_VALID_25); ++ ent->is_age = !!(mac_vid & ARLTBL_AGE_25); ++ ent->is_static = !!(mac_vid & ARLTBL_STATIC_25); ++ u64_to_ether_addr(mac_vid, ent->mac); ++ ent->vid = mac_vid >> ARLTBL_VID_S_65; ++} ++ + static inline void b53_arl_from_entry(u64 *mac_vid, u32 *fwd_entry, + const struct b53_arl_entry *ent) + { +@@ -331,6 +344,22 @@ static inline void b53_arl_from_entry(u6 + *fwd_entry |= ARLTBL_AGE; + } + ++static inline void b53_arl_from_entry_25(u64 *mac_vid, ++ const struct b53_arl_entry *ent) ++{ ++ *mac_vid = ether_addr_to_u64(ent->mac); ++ *mac_vid |= (u64)(ent->port & ARLTBL_DATA_PORT_ID_MASK_25) << ++ ARLTBL_DATA_PORT_ID_S_25; ++ *mac_vid |= (u64)(ent->vid & ARLTBL_VID_MASK_25) << ++ ARLTBL_VID_S_65; ++ if (ent->is_valid) ++ *mac_vid |= ARLTBL_VALID_25; ++ if (ent->is_static) ++ *mac_vid |= ARLTBL_STATIC_25; ++ if (ent->is_age) ++ *mac_vid |= ARLTBL_AGE_25; ++} ++ + #ifdef CONFIG_BCM47XX + + #include +--- a/drivers/net/dsa/b53/b53_regs.h ++++ b/drivers/net/dsa/b53/b53_regs.h +@@ -328,9 +328,10 @@ + #define ARLTBL_VID_MASK 0xfff + #define ARLTBL_DATA_PORT_ID_S_25 48 + #define ARLTBL_DATA_PORT_ID_MASK_25 0xf +-#define ARLTBL_AGE_25 BIT(61) +-#define ARLTBL_STATIC_25 BIT(62) +-#define ARLTBL_VALID_25 BIT(63) ++#define ARLTBL_VID_S_65 53 ++#define ARLTBL_AGE_25 BIT_ULL(61) ++#define ARLTBL_STATIC_25 BIT_ULL(62) ++#define ARLTBL_VALID_25 BIT_ULL(63) + + /* ARL Table Data Entry N Registers (32 bit) */ + #define B53_ARLTBL_DATA_ENTRY(n) ((0x10 * (n)) + 0x18) diff --git a/target/linux/generic/backport-6.12/611-05-v6.17-net-dsa-b53-prevent-FAST_AGE-access-on-BCM5325.patch b/target/linux/generic/backport-6.12/611-05-v6.17-net-dsa-b53-prevent-FAST_AGE-access-on-BCM5325.patch new file mode 100644 index 00000000000..7397e3323c7 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-05-v6.17-net-dsa-b53-prevent-FAST_AGE-access-on-BCM5325.patch @@ -0,0 +1,51 @@ +From 9b6c767c312b4709e9aeb2314a6b47863e7fb72d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Sat, 14 Jun 2025 09:59:52 +0200 +Subject: [PATCH] net: dsa: b53: prevent FAST_AGE access on BCM5325 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BCM5325 doesn't implement FAST_AGE registers so we should avoid reading or +writing them. + +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250614080000.1884236-7-noltari@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -492,6 +492,9 @@ static int b53_flush_arl(struct b53_devi + { + unsigned int i; + ++ if (is5325(dev)) ++ return 0; ++ + b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, + FAST_AGE_DONE | FAST_AGE_DYNAMIC | mask); + +@@ -516,6 +519,9 @@ out: + + static int b53_fast_age_port(struct b53_device *dev, int port) + { ++ if (is5325(dev)) ++ return 0; ++ + b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_PORT_CTRL, port); + + return b53_flush_arl(dev, FAST_AGE_PORT); +@@ -523,6 +529,9 @@ static int b53_fast_age_port(struct b53_ + + static int b53_fast_age_vlan(struct b53_device *dev, u16 vid) + { ++ if (is5325(dev)) ++ return 0; ++ + b53_write16(dev, B53_CTRL_PAGE, B53_FAST_AGE_VID_CTRL, vid); + + return b53_flush_arl(dev, FAST_AGE_VLAN); diff --git a/target/linux/generic/backport-6.12/611-06-v6.17-net-dsa-b53-prevent-BRCM_HDR-access-on-older-devices.patch b/target/linux/generic/backport-6.12/611-06-v6.17-net-dsa-b53-prevent-BRCM_HDR-access-on-older-devices.patch new file mode 100644 index 00000000000..6ea836734a5 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-06-v6.17-net-dsa-b53-prevent-BRCM_HDR-access-on-older-devices.patch @@ -0,0 +1,33 @@ +From e17813968b08b1b09bf80699223dea48851cbd07 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Sat, 14 Jun 2025 09:59:56 +0200 +Subject: [PATCH] net: dsa: b53: prevent BRCM_HDR access on older devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Older switches don't implement BRCM_HDR register so we should avoid +reading or writing it. + +Reviewed-by: Florian Fainelli +Signed-off-by: Álvaro Fernández Rojas +Link: https://patch.msgid.link/20250614080000.1884236-11-noltari@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -747,6 +747,11 @@ void b53_brcm_hdr_setup(struct dsa_switc + hdr_ctl |= GC_FRM_MGMT_PORT_M; + b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, hdr_ctl); + ++ /* B53_BRCM_HDR not present on devices with legacy tags */ ++ if (dev->tag_protocol == DSA_TAG_PROTO_BRCM_LEGACY || ++ dev->tag_protocol == DSA_TAG_PROTO_BRCM_LEGACY_FCS) ++ return; ++ + /* Enable Broadcom tags for IMP port */ + b53_read8(dev, B53_MGMT_PAGE, B53_BRCM_HDR, &hdr_ctl); + if (tag_en) diff --git a/target/linux/generic/backport-6.12/611-07-v6.17-net-dsa-b53-fix-unicast-multicast-flooding-on-BCM5325.patch b/target/linux/generic/backport-6.12/611-07-v6.17-net-dsa-b53-fix-unicast-multicast-flooding-on-BCM5325.patch new file mode 100644 index 00000000000..6b9c86ed0c9 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-07-v6.17-net-dsa-b53-fix-unicast-multicast-flooding-on-BCM5325.patch @@ -0,0 +1,128 @@ +From 651c9e71ffe44e99b5a9b011271c2117f0353b32 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Sat, 14 Jun 2025 09:59:58 +0200 +Subject: [PATCH] net: dsa: b53: fix unicast/multicast flooding on BCM5325 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BCM5325 doesn't implement UC_FLOOD_MASK, MC_FLOOD_MASK and IPMC_FLOOD_MASK +registers. +This has to be handled differently with other pages and registers. + +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250614080000.1884236-13-noltari@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 60 ++++++++++++++++++++++---------- + drivers/net/dsa/b53/b53_regs.h | 13 +++++++ + 2 files changed, 55 insertions(+), 18 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -564,12 +564,24 @@ static void b53_port_set_ucast_flood(str + { + u16 uc; + +- b53_read16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, &uc); +- if (unicast) +- uc |= BIT(port); +- else +- uc &= ~BIT(port); +- b53_write16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, uc); ++ if (is5325(dev)) { ++ if (port == B53_CPU_PORT_25) ++ port = B53_CPU_PORT; ++ ++ b53_read16(dev, B53_IEEE_PAGE, B53_IEEE_UCAST_DLF, &uc); ++ if (unicast) ++ uc |= BIT(port) | B53_IEEE_UCAST_DROP_EN; ++ else ++ uc &= ~BIT(port); ++ b53_write16(dev, B53_IEEE_PAGE, B53_IEEE_UCAST_DLF, uc); ++ } else { ++ b53_read16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, &uc); ++ if (unicast) ++ uc |= BIT(port); ++ else ++ uc &= ~BIT(port); ++ b53_write16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, uc); ++ } + } + + static void b53_port_set_mcast_flood(struct b53_device *dev, int port, +@@ -577,19 +589,31 @@ static void b53_port_set_mcast_flood(str + { + u16 mc; + +- b53_read16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, &mc); +- if (multicast) +- mc |= BIT(port); +- else +- mc &= ~BIT(port); +- b53_write16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, mc); +- +- b53_read16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, &mc); +- if (multicast) +- mc |= BIT(port); +- else +- mc &= ~BIT(port); +- b53_write16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, mc); ++ if (is5325(dev)) { ++ if (port == B53_CPU_PORT_25) ++ port = B53_CPU_PORT; ++ ++ b53_read16(dev, B53_IEEE_PAGE, B53_IEEE_MCAST_DLF, &mc); ++ if (multicast) ++ mc |= BIT(port) | B53_IEEE_MCAST_DROP_EN; ++ else ++ mc &= ~BIT(port); ++ b53_write16(dev, B53_IEEE_PAGE, B53_IEEE_MCAST_DLF, mc); ++ } else { ++ b53_read16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, &mc); ++ if (multicast) ++ mc |= BIT(port); ++ else ++ mc &= ~BIT(port); ++ b53_write16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, mc); ++ ++ b53_read16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, &mc); ++ if (multicast) ++ mc |= BIT(port); ++ else ++ mc &= ~BIT(port); ++ b53_write16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, mc); ++ } + } + + static void b53_port_set_learning(struct b53_device *dev, int port, +--- a/drivers/net/dsa/b53/b53_regs.h ++++ b/drivers/net/dsa/b53/b53_regs.h +@@ -29,6 +29,7 @@ + #define B53_ARLIO_PAGE 0x05 /* ARL Access */ + #define B53_FRAMEBUF_PAGE 0x06 /* Management frame access */ + #define B53_MEM_ACCESS_PAGE 0x08 /* Memory access */ ++#define B53_IEEE_PAGE 0x0a /* IEEE 802.1X */ + + /* PHY Registers */ + #define B53_PORT_MII_PAGE(i) (0x10 + (i)) /* Port i MII Registers */ +@@ -371,6 +372,18 @@ + #define B53_ARL_SRCH_RSTL(x) (B53_ARL_SRCH_RSTL_0 + ((x) * 0x10)) + + /************************************************************************* ++ * IEEE 802.1X Registers ++ *************************************************************************/ ++ ++/* Multicast DLF Drop Control register (16 bit) */ ++#define B53_IEEE_MCAST_DLF 0x94 ++#define B53_IEEE_MCAST_DROP_EN BIT(11) ++ ++/* Unicast DLF Drop Control register (16 bit) */ ++#define B53_IEEE_UCAST_DLF 0x96 ++#define B53_IEEE_UCAST_DROP_EN BIT(11) ++ ++/************************************************************************* + * Port VLAN Registers + *************************************************************************/ + diff --git a/target/linux/generic/backport-6.12/611-08-v6.18-net-dsa-b53-Add-phy_enable-phy_disable-methods.patch b/target/linux/generic/backport-6.12/611-08-v6.18-net-dsa-b53-Add-phy_enable-phy_disable-methods.patch new file mode 100644 index 00000000000..14106e3dd24 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-08-v6.18-net-dsa-b53-Add-phy_enable-phy_disable-methods.patch @@ -0,0 +1,50 @@ +From be7a79145d85af1a9d65a45560b9243b13a67782 Mon Sep 17 00:00:00 2001 +From: Kyle Hendry +Date: Wed, 23 Jul 2025 20:52:40 -0700 +Subject: [PATCH] net: dsa: b53: Add phy_enable(), phy_disable() methods + +Add phy enable/disable to b53 ops to be called when +enabling/disabling ports. + +Signed-off-by: Kyle Hendry +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250724035300.20497-2-kylehendrydev@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 6 ++++++ + drivers/net/dsa/b53/b53_priv.h | 2 ++ + 2 files changed, 8 insertions(+) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -689,6 +689,9 @@ int b53_enable_port(struct dsa_switch *d + + cpu_port = dsa_to_port(ds, port)->cpu_dp->index; + ++ if (dev->ops->phy_enable) ++ dev->ops->phy_enable(dev, port); ++ + if (dev->ops->irq_enable) + ret = dev->ops->irq_enable(dev, port); + if (ret) +@@ -727,6 +730,9 @@ void b53_disable_port(struct dsa_switch + reg |= PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE; + b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg); + ++ if (dev->ops->phy_disable) ++ dev->ops->phy_disable(dev, port); ++ + if (dev->ops->irq_disable) + dev->ops->irq_disable(dev, port); + } +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -45,6 +45,8 @@ struct b53_io_ops { + int (*phy_write16)(struct b53_device *dev, int addr, int reg, u16 value); + int (*irq_enable)(struct b53_device *dev, int port); + void (*irq_disable)(struct b53_device *dev, int port); ++ void (*phy_enable)(struct b53_device *dev, int port); ++ void (*phy_disable)(struct b53_device *dev, int port); + void (*phylink_get_caps)(struct b53_device *dev, int port, + struct phylink_config *config); + struct phylink_pcs *(*phylink_mac_select_pcs)(struct b53_device *dev, diff --git a/target/linux/generic/backport-6.12/611-09-v6.18-net-dsa-b53-Define-chip-IDs-for-more-bcm63xx-SoCs.patch b/target/linux/generic/backport-6.12/611-09-v6.18-net-dsa-b53-Define-chip-IDs-for-more-bcm63xx-SoCs.patch new file mode 100644 index 00000000000..0e039168ad7 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-09-v6.18-net-dsa-b53-Define-chip-IDs-for-more-bcm63xx-SoCs.patch @@ -0,0 +1,124 @@ +From fcf02a462fab52fbfcb24e617dd940745afd0dff Mon Sep 17 00:00:00 2001 +From: Kyle Hendry +Date: Wed, 23 Jul 2025 20:52:42 -0700 +Subject: [PATCH] net: dsa: b53: Define chip IDs for more bcm63xx SoCs + +Add defines for bcm6318, bcm6328, bcm6362, bcm6368 chip IDs, +update tables and switch init. + +Signed-off-by: Kyle Hendry +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250724035300.20497-4-kylehendrydev@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 21 ++++++--------------- + drivers/net/dsa/b53/b53_mmap.c | 8 ++++---- + drivers/net/dsa/b53/b53_priv.h | 13 +++++++++++-- + 3 files changed, 21 insertions(+), 21 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1419,7 +1419,7 @@ static void b53_adjust_63xx_rgmii(struct + b53_read8(dev, B53_CTRL_PAGE, B53_RGMII_CTRL_P(port), &rgmii_ctrl); + rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); + +- if (is63268(dev)) ++ if (is6318_268(dev)) + rgmii_ctrl |= RGMII_CTRL_MII_OVERRIDE; + + rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII; +@@ -2804,19 +2804,6 @@ static const struct b53_chip_data b53_sw + .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, + }, + { +- .chip_id = BCM63268_DEVICE_ID, +- .dev_name = "BCM63268", +- .vlans = 4096, +- .enabled_ports = 0, /* pdata must provide them */ +- .arl_bins = 4, +- .arl_buckets = 1024, +- .imp_port = 8, +- .vta_regs = B53_VTA_REGS_63XX, +- .duplex_reg = B53_DUPLEX_STAT_63XX, +- .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX, +- .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, +- }, +- { + .chip_id = BCM53010_DEVICE_ID, + .dev_name = "BCM53010", + .vlans = 4096, +@@ -2965,13 +2952,17 @@ static const struct b53_chip_data b53_sw + + static int b53_switch_init(struct b53_device *dev) + { ++ u32 chip_id = dev->chip_id; + unsigned int i; + int ret; + ++ if (is63xx(dev)) ++ chip_id = BCM63XX_DEVICE_ID; ++ + for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) { + const struct b53_chip_data *chip = &b53_switch_chips[i]; + +- if (chip->chip_id == dev->chip_id) { ++ if (chip->chip_id == chip_id) { + if (!dev->enabled_ports) + dev->enabled_ports = chip->enabled_ports; + dev->name = chip->dev_name; +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -348,16 +348,16 @@ static const struct of_device_id b53_mma + .data = (void *)BCM63XX_DEVICE_ID, + }, { + .compatible = "brcm,bcm6318-switch", +- .data = (void *)BCM63268_DEVICE_ID, ++ .data = (void *)BCM6318_DEVICE_ID, + }, { + .compatible = "brcm,bcm6328-switch", +- .data = (void *)BCM63XX_DEVICE_ID, ++ .data = (void *)BCM6328_DEVICE_ID, + }, { + .compatible = "brcm,bcm6362-switch", +- .data = (void *)BCM63XX_DEVICE_ID, ++ .data = (void *)BCM6362_DEVICE_ID, + }, { + .compatible = "brcm,bcm6368-switch", +- .data = (void *)BCM63XX_DEVICE_ID, ++ .data = (void *)BCM6368_DEVICE_ID, + }, { + .compatible = "brcm,bcm63268-switch", + .data = (void *)BCM63268_DEVICE_ID, +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -73,6 +73,10 @@ enum { + BCM53125_DEVICE_ID = 0x53125, + BCM53128_DEVICE_ID = 0x53128, + BCM63XX_DEVICE_ID = 0x6300, ++ BCM6318_DEVICE_ID = 0x6318, ++ BCM6328_DEVICE_ID = 0x6328, ++ BCM6362_DEVICE_ID = 0x6362, ++ BCM6368_DEVICE_ID = 0x6368, + BCM63268_DEVICE_ID = 0x63268, + BCM53010_DEVICE_ID = 0x53010, + BCM53011_DEVICE_ID = 0x53011, +@@ -220,12 +224,17 @@ static inline int is531x5(struct b53_dev + static inline int is63xx(struct b53_device *dev) + { + return dev->chip_id == BCM63XX_DEVICE_ID || ++ dev->chip_id == BCM6318_DEVICE_ID || ++ dev->chip_id == BCM6328_DEVICE_ID || ++ dev->chip_id == BCM6362_DEVICE_ID || ++ dev->chip_id == BCM6368_DEVICE_ID || + dev->chip_id == BCM63268_DEVICE_ID; + } + +-static inline int is63268(struct b53_device *dev) ++static inline int is6318_268(struct b53_device *dev) + { +- return dev->chip_id == BCM63268_DEVICE_ID; ++ return dev->chip_id == BCM6318_DEVICE_ID || ++ dev->chip_id == BCM63268_DEVICE_ID; + } + + static inline int is5301x(struct b53_device *dev) diff --git a/target/linux/generic/backport-6.12/611-10-v6.18-net-dsa-b53-mmap-Add-syscon-reference-and-register-layout-for-bcm63268.patch b/target/linux/generic/backport-6.12/611-10-v6.18-net-dsa-b53-mmap-Add-syscon-reference-and-register-layout-for-bcm63268.patch new file mode 100644 index 00000000000..92e540d3845 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-10-v6.18-net-dsa-b53-mmap-Add-syscon-reference-and-register-layout-for-bcm63268.patch @@ -0,0 +1,69 @@ +From aed2aaa3c963f8aabbfa061a177022fee826ebfb Mon Sep 17 00:00:00 2001 +From: Kyle Hendry +Date: Wed, 23 Jul 2025 20:52:43 -0700 +Subject: [PATCH] net: dsa: b53: mmap: Add syscon reference and register layout + for bcm63268 + +On bcm63xx SoCs there are registers that control the PHYs in +the GPIO controller. Allow the b53 driver to access them +by passing in the syscon through the device tree. + +Add a structure to describe the ephy control register +and add register info for bcm63268. + +Signed-off-by: Kyle Hendry +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250724035300.20497-5-kylehendrydev@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_mmap.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -21,13 +21,32 @@ + #include + #include + #include ++#include + #include + #include + + #include "b53_priv.h" + ++struct b53_phy_info { ++ u32 ephy_enable_mask; ++ u32 ephy_port_mask; ++ u32 ephy_bias_bit; ++ const u32 *ephy_offset; ++}; ++ + struct b53_mmap_priv { + void __iomem *regs; ++ struct regmap *gpio_ctrl; ++ const struct b53_phy_info *phy_info; ++}; ++ ++static const u32 bcm63268_ephy_offsets[] = {4, 9, 14}; ++ ++static const struct b53_phy_info bcm63268_ephy_info = { ++ .ephy_enable_mask = GENMASK(4, 0), ++ .ephy_port_mask = GENMASK((ARRAY_SIZE(bcm63268_ephy_offsets) - 1), 0), ++ .ephy_bias_bit = 24, ++ .ephy_offset = bcm63268_ephy_offsets, + }; + + static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) +@@ -313,6 +332,12 @@ static int b53_mmap_probe(struct platfor + + priv->regs = pdata->regs; + ++ priv->gpio_ctrl = syscon_regmap_lookup_by_phandle(np, "brcm,gpio-ctrl"); ++ if (!IS_ERR(priv->gpio_ctrl)) { ++ if (pdata->chip_id == BCM63268_DEVICE_ID) ++ priv->phy_info = &bcm63268_ephy_info; ++ } ++ + dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv); + if (!dev) + return -ENOMEM; diff --git a/target/linux/generic/backport-6.12/611-11-v6.18-net-dsa-b53-mmap-Add-register-layout-for-bcm6318.patch b/target/linux/generic/backport-6.12/611-11-v6.18-net-dsa-b53-mmap-Add-register-layout-for-bcm6318.patch new file mode 100644 index 00000000000..f6c1cbaf016 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-11-v6.18-net-dsa-b53-mmap-Add-register-layout-for-bcm6318.patch @@ -0,0 +1,47 @@ +From c251304ab021ff21c77e83e0babcb9eb76f8787a Mon Sep 17 00:00:00 2001 +From: Kyle Hendry +Date: Wed, 23 Jul 2025 20:52:44 -0700 +Subject: [PATCH] net: dsa: b53: mmap: Add register layout for bcm6318 + +Add ephy register info for bcm6318, which also applies to +bcm6328 and bcm6362. + +Signed-off-by: Kyle Hendry +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250724035300.20497-6-kylehendrydev@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_mmap.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -40,6 +40,15 @@ struct b53_mmap_priv { + const struct b53_phy_info *phy_info; + }; + ++static const u32 bcm6318_ephy_offsets[] = {4, 5, 6, 7}; ++ ++static const struct b53_phy_info bcm6318_ephy_info = { ++ .ephy_enable_mask = BIT(0) | BIT(4) | BIT(8) | BIT(12) | BIT(16), ++ .ephy_port_mask = GENMASK((ARRAY_SIZE(bcm6318_ephy_offsets) - 1), 0), ++ .ephy_bias_bit = 24, ++ .ephy_offset = bcm6318_ephy_offsets, ++}; ++ + static const u32 bcm63268_ephy_offsets[] = {4, 9, 14}; + + static const struct b53_phy_info bcm63268_ephy_info = { +@@ -334,7 +343,11 @@ static int b53_mmap_probe(struct platfor + + priv->gpio_ctrl = syscon_regmap_lookup_by_phandle(np, "brcm,gpio-ctrl"); + if (!IS_ERR(priv->gpio_ctrl)) { +- if (pdata->chip_id == BCM63268_DEVICE_ID) ++ if (pdata->chip_id == BCM6318_DEVICE_ID || ++ pdata->chip_id == BCM6328_DEVICE_ID || ++ pdata->chip_id == BCM6362_DEVICE_ID) ++ priv->phy_info = &bcm6318_ephy_info; ++ else if (pdata->chip_id == BCM63268_DEVICE_ID) + priv->phy_info = &bcm63268_ephy_info; + } + diff --git a/target/linux/generic/backport-6.12/611-12-v6.18-net-dsa-b53-mmap-Add-register-layout-for-bcm6368.patch b/target/linux/generic/backport-6.12/611-12-v6.18-net-dsa-b53-mmap-Add-register-layout-for-bcm6368.patch new file mode 100644 index 00000000000..f703804a400 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-12-v6.18-net-dsa-b53-mmap-Add-register-layout-for-bcm6368.patch @@ -0,0 +1,42 @@ +From e8e13073dff7052b144d002bae2cfe9ddfa27e2a Mon Sep 17 00:00:00 2001 +From: Kyle Hendry +Date: Wed, 23 Jul 2025 20:52:45 -0700 +Subject: [PATCH] net: dsa: b53: mmap: Add register layout for bcm6368 + +Add ephy register info for bcm6368. + +Signed-off-by: Kyle Hendry +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250724035300.20497-7-kylehendrydev@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_mmap.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -49,6 +49,15 @@ static const struct b53_phy_info bcm6318 + .ephy_offset = bcm6318_ephy_offsets, + }; + ++static const u32 bcm6368_ephy_offsets[] = {2, 3, 4, 5}; ++ ++static const struct b53_phy_info bcm6368_ephy_info = { ++ .ephy_enable_mask = BIT(0), ++ .ephy_port_mask = GENMASK((ARRAY_SIZE(bcm6368_ephy_offsets) - 1), 0), ++ .ephy_bias_bit = 0, ++ .ephy_offset = bcm6368_ephy_offsets, ++}; ++ + static const u32 bcm63268_ephy_offsets[] = {4, 9, 14}; + + static const struct b53_phy_info bcm63268_ephy_info = { +@@ -347,6 +356,8 @@ static int b53_mmap_probe(struct platfor + pdata->chip_id == BCM6328_DEVICE_ID || + pdata->chip_id == BCM6362_DEVICE_ID) + priv->phy_info = &bcm6318_ephy_info; ++ else if (pdata->chip_id == BCM6368_DEVICE_ID) ++ priv->phy_info = &bcm6368_ephy_info; + else if (pdata->chip_id == BCM63268_DEVICE_ID) + priv->phy_info = &bcm63268_ephy_info; + } diff --git a/target/linux/generic/backport-6.12/611-13-v6.18-net-dsa-b53-mmap-Implement-bcm63xx-ephy-power-control.patch b/target/linux/generic/backport-6.12/611-13-v6.18-net-dsa-b53-mmap-Implement-bcm63xx-ephy-power-control.patch new file mode 100644 index 00000000000..bf3aeb70470 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-13-v6.18-net-dsa-b53-mmap-Implement-bcm63xx-ephy-power-control.patch @@ -0,0 +1,100 @@ +From 5ac00023852d960528a0c1d10ae6c17893fc4113 Mon Sep 17 00:00:00 2001 +From: Kyle Hendry +Date: Wed, 23 Jul 2025 20:52:46 -0700 +Subject: [PATCH] net: dsa: b53: mmap: Implement bcm63xx ephy power control + +Implement the phy enable/disable calls for b53 mmap, and +set the power down registers in the ephy control register +appropriately. + +Signed-off-by: Kyle Hendry +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250724035300.20497-8-kylehendrydev@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_mmap.c | 50 ++++++++++++++++++++++++++++++++++ + 1 file changed, 50 insertions(+) + +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -24,9 +24,12 @@ + #include + #include + #include ++#include + + #include "b53_priv.h" + ++#define BCM63XX_EPHY_REG 0x3C ++ + struct b53_phy_info { + u32 ephy_enable_mask; + u32 ephy_port_mask; +@@ -38,6 +41,7 @@ struct b53_mmap_priv { + void __iomem *regs; + struct regmap *gpio_ctrl; + const struct b53_phy_info *phy_info; ++ u32 phys_enabled; + }; + + static const u32 bcm6318_ephy_offsets[] = {4, 5, 6, 7}; +@@ -266,6 +270,50 @@ static int b53_mmap_phy_write16(struct b + return -EIO; + } + ++static int bcm63xx_ephy_set(struct b53_device *dev, int port, bool enable) ++{ ++ struct b53_mmap_priv *priv = dev->priv; ++ const struct b53_phy_info *info = priv->phy_info; ++ struct regmap *gpio_ctrl = priv->gpio_ctrl; ++ u32 mask, val; ++ ++ if (enable) { ++ mask = (info->ephy_enable_mask << info->ephy_offset[port]) ++ | BIT(info->ephy_bias_bit); ++ val = 0; ++ } else { ++ mask = (info->ephy_enable_mask << info->ephy_offset[port]); ++ if (!((priv->phys_enabled & ~BIT(port)) & info->ephy_port_mask)) ++ mask |= BIT(info->ephy_bias_bit); ++ val = mask; ++ } ++ return regmap_update_bits(gpio_ctrl, BCM63XX_EPHY_REG, mask, val); ++} ++ ++static void b53_mmap_phy_enable(struct b53_device *dev, int port) ++{ ++ struct b53_mmap_priv *priv = dev->priv; ++ int ret = 0; ++ ++ if (priv->phy_info && (BIT(port) & priv->phy_info->ephy_port_mask)) ++ ret = bcm63xx_ephy_set(dev, port, true); ++ ++ if (!ret) ++ priv->phys_enabled |= BIT(port); ++} ++ ++static void b53_mmap_phy_disable(struct b53_device *dev, int port) ++{ ++ struct b53_mmap_priv *priv = dev->priv; ++ int ret = 0; ++ ++ if (priv->phy_info && (BIT(port) & priv->phy_info->ephy_port_mask)) ++ ret = bcm63xx_ephy_set(dev, port, false); ++ ++ if (!ret) ++ priv->phys_enabled &= ~BIT(port); ++} ++ + static const struct b53_io_ops b53_mmap_ops = { + .read8 = b53_mmap_read8, + .read16 = b53_mmap_read16, +@@ -279,6 +327,8 @@ static const struct b53_io_ops b53_mmap_ + .write64 = b53_mmap_write64, + .phy_read16 = b53_mmap_phy_read16, + .phy_write16 = b53_mmap_phy_write16, ++ .phy_enable = b53_mmap_phy_enable, ++ .phy_disable = b53_mmap_phy_disable, + }; + + static int b53_mmap_probe_of(struct platform_device *pdev, diff --git a/target/linux/generic/backport-6.12/611-14-v6.18-net-dsa-b53-mmap-Add-gphy-port-to-phy-info-for-bcm63268.patch b/target/linux/generic/backport-6.12/611-14-v6.18-net-dsa-b53-mmap-Add-gphy-port-to-phy-info-for-bcm63268.patch new file mode 100644 index 00000000000..9aa99d234e0 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-14-v6.18-net-dsa-b53-mmap-Add-gphy-port-to-phy-info-for-bcm63268.patch @@ -0,0 +1,33 @@ +From 7f95f04fe1903a31b61085e3ab1b4730f9d72941 Mon Sep 17 00:00:00 2001 +From: Kyle Hendry +Date: Wed, 13 Aug 2025 17:25:27 -0700 +Subject: [PATCH] net: dsa: b53: mmap: Add gphy port to phy info for bcm63268 + +Add gphy mask to bcm63xx phy info struct and add data for bcm63268 + +Signed-off-by: Kyle Hendry +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250814002530.5866-2-kylehendrydev@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_mmap.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -31,6 +31,7 @@ + #define BCM63XX_EPHY_REG 0x3C + + struct b53_phy_info { ++ u32 gphy_port_mask; + u32 ephy_enable_mask; + u32 ephy_port_mask; + u32 ephy_bias_bit; +@@ -65,6 +66,7 @@ static const struct b53_phy_info bcm6368 + static const u32 bcm63268_ephy_offsets[] = {4, 9, 14}; + + static const struct b53_phy_info bcm63268_ephy_info = { ++ .gphy_port_mask = BIT(3), + .ephy_enable_mask = GENMASK(4, 0), + .ephy_port_mask = GENMASK((ARRAY_SIZE(bcm63268_ephy_offsets) - 1), 0), + .ephy_bias_bit = 24, diff --git a/target/linux/generic/backport-6.12/611-15-v6.18-net-dsa-b53-mmap-Implement-bcm63268-gphy-power-control.patch b/target/linux/generic/backport-6.12/611-15-v6.18-net-dsa-b53-mmap-Implement-bcm63268-gphy-power-control.patch new file mode 100644 index 00000000000..8dda51e19ee --- /dev/null +++ b/target/linux/generic/backport-6.12/611-15-v6.18-net-dsa-b53-mmap-Implement-bcm63268-gphy-power-control.patch @@ -0,0 +1,77 @@ +From 61730ac10ba90c52563861a0119504f6a9be9868 Mon Sep 17 00:00:00 2001 +From: Kyle Hendry +Date: Wed, 13 Aug 2025 17:25:28 -0700 +Subject: [PATCH] net: dsa: b53: mmap: Implement bcm63268 gphy power control + +Add check for gphy in enable/disable phy calls and set power bits +in gphy control register. + +Signed-off-by: Kyle Hendry +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250814002530.5866-3-kylehendrydev@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_mmap.c | 33 +++++++++++++++++++++++++++++---- + 1 file changed, 29 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -29,6 +29,10 @@ + #include "b53_priv.h" + + #define BCM63XX_EPHY_REG 0x3C ++#define BCM63268_GPHY_REG 0x54 ++ ++#define GPHY_CTRL_LOW_PWR BIT(3) ++#define GPHY_CTRL_IDDQ_BIAS BIT(0) + + struct b53_phy_info { + u32 gphy_port_mask; +@@ -292,13 +296,30 @@ static int bcm63xx_ephy_set(struct b53_d + return regmap_update_bits(gpio_ctrl, BCM63XX_EPHY_REG, mask, val); + } + ++static int bcm63268_gphy_set(struct b53_device *dev, bool enable) ++{ ++ struct b53_mmap_priv *priv = dev->priv; ++ struct regmap *gpio_ctrl = priv->gpio_ctrl; ++ u32 mask = GPHY_CTRL_IDDQ_BIAS | GPHY_CTRL_LOW_PWR; ++ u32 val = 0; ++ ++ if (!enable) ++ val = mask; ++ ++ return regmap_update_bits(gpio_ctrl, BCM63268_GPHY_REG, mask, val); ++} ++ + static void b53_mmap_phy_enable(struct b53_device *dev, int port) + { + struct b53_mmap_priv *priv = dev->priv; + int ret = 0; + +- if (priv->phy_info && (BIT(port) & priv->phy_info->ephy_port_mask)) +- ret = bcm63xx_ephy_set(dev, port, true); ++ if (priv->phy_info) { ++ if (BIT(port) & priv->phy_info->ephy_port_mask) ++ ret = bcm63xx_ephy_set(dev, port, true); ++ else if (BIT(port) & priv->phy_info->gphy_port_mask) ++ ret = bcm63268_gphy_set(dev, true); ++ } + + if (!ret) + priv->phys_enabled |= BIT(port); +@@ -309,8 +330,12 @@ static void b53_mmap_phy_disable(struct + struct b53_mmap_priv *priv = dev->priv; + int ret = 0; + +- if (priv->phy_info && (BIT(port) & priv->phy_info->ephy_port_mask)) +- ret = bcm63xx_ephy_set(dev, port, false); ++ if (priv->phy_info) { ++ if (BIT(port) & priv->phy_info->ephy_port_mask) ++ ret = bcm63xx_ephy_set(dev, port, false); ++ else if (BIT(port) & priv->phy_info->gphy_port_mask) ++ ret = bcm63268_gphy_set(dev, false); ++ } + + if (!ret) + priv->phys_enabled &= ~BIT(port); diff --git a/target/linux/generic/backport-6.12/611-16-v6.18-net-dsa-b53-fix-reserved-register-access-in-b53_fdb_dump.patch b/target/linux/generic/backport-6.12/611-16-v6.18-net-dsa-b53-fix-reserved-register-access-in-b53_fdb_dump.patch new file mode 100644 index 00000000000..5190cf908fc --- /dev/null +++ b/target/linux/generic/backport-6.12/611-16-v6.18-net-dsa-b53-fix-reserved-register-access-in-b53_fdb_dump.patch @@ -0,0 +1,72 @@ +From 89eb9a62aed77b409663ba1eac152e8f758815b7 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 15 Aug 2025 22:18:09 +0200 +Subject: [PATCH] net: dsa: b53: fix reserved register access in b53_fdb_dump() + +When BCM5325 support was added in c45655386e53 ("net: dsa: b53: add +support for FDB operations on 5325/5365"), the register used for ARL access +was made conditional on the chip. + +But in b53_fdb_dump(), instead of the register argument the page +argument was replaced, causing it to write to a reserved page 0x50 on +!BCM5325*. Writing to this page seems to completely lock the switch up: + +[ 89.680000] b53-switch spi0.1 lan2: Link is Down +[ 89.680000] WARNING: CPU: 1 PID: 26 at drivers/net/phy/phy.c:1350 _phy_state_machine+0x1bc/0x454 +[ 89.720000] phy_check_link_status+0x0/0x114: returned: -5 +[ 89.730000] Modules linked in: nft_fib_inet nf_flow_table_inet nft_reject_ipv6 nft_reject_ipv4 nft_reject_inet nft_reject nft_redir nft_quota nft_numgen nft_nat nft_masq nft_log nft_limit nft_hash nft_flow_offload nft_fib_ipv6 nft_fib_ipv4 nft_fib nft_ct nft_chain_nat nf_tables nf_nat nf_flow_table nf_conntrack nfnetlink nf_reject_ipv6 nf_reject_ipv4 nf_log_syslog nf_defrag_ipv6 nf_defrag_ipv4 cls_flower sch_tbf sch_ingress sch_htb sch_hfsc em_u32 cls_u32 cls_route cls_matchall cls_fw cls_flow cls_basic act_skbedit act_mirred act_gact vrf md5 crc32c_cryptoapi +[ 89.780000] CPU: 1 UID: 0 PID: 26 Comm: kworker/u10:0 Tainted: G W 6.16.0-rc1+ #0 NONE +[ 89.780000] Tainted: [W]=WARN +[ 89.780000] Hardware name: Netgear DGND3700 v1 +[ 89.780000] Workqueue: events_power_efficient phy_state_machine +[ 89.780000] Stack : 809c762c 8006b050 00000001 820a9ce3 0000114c 000affff 805d22d0 8200ba00 +[ 89.780000] 82005000 6576656e 74735f70 6f776572 5f656666 10008b00 820a9cb8 82088700 +[ 89.780000] 00000000 00000000 809c762c 820a9a98 00000000 00000000 ffffefff 80a7a76c +[ 89.780000] 80a70000 820a9af8 80a70000 80a70000 80a70000 00000000 809c762c 820a9dd4 +[ 89.780000] 00000000 805d1494 80a029e4 80a70000 00000003 00000000 00000004 81a60004 +[ 89.780000] ... +[ 89.780000] Call Trace: +[ 89.780000] [<800228b8>] show_stack+0x38/0x118 +[ 89.780000] [<8001afc4>] dump_stack_lvl+0x6c/0xac +[ 89.780000] [<80046b90>] __warn+0x9c/0x114 +[ 89.780000] [<80046da8>] warn_slowpath_fmt+0x1a0/0x1b0 +[ 89.780000] [<805d1494>] _phy_state_machine+0x1bc/0x454 +[ 89.780000] [<805d22fc>] phy_state_machine+0x2c/0x70 +[ 89.780000] [<80066b08>] process_one_work+0x1e8/0x3e0 +[ 89.780000] [<80067a1c>] worker_thread+0x354/0x4e4 +[ 89.780000] [<800706cc>] kthread+0x130/0x274 +[ 89.780000] [<8001d808>] ret_from_kernel_thread+0x14/0x1c + +And any further accesses fail: + +[ 120.790000] b53-switch spi0.1: timeout waiting for ARL to finish: 0x81 +[ 120.800000] b53-switch spi0.1: port 2 failed to add 2c:b0:5d:27:9a:bd vid 3 to fdb: -145 +[ 121.010000] b53-switch spi0.1: timeout waiting for ARL to finish: 0xbf +[ 121.020000] b53-switch spi0.1: port 3 failed to add 2c:b0:5d:27:9a:bd vid 3 to fdb: -145 + +Restore the correct page B53_ARLIO_PAGE again, and move the offset +argument to the correct place. + +*On BCM5325, this became a write to the MIB page of Port 1. Still +a reserved offset, but likely less brokenness from that write. + +Fixes: c45655386e53 ("net: dsa: b53: add support for FDB operations on 5325/5365") +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250815201809.549195-1-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2100,7 +2100,7 @@ int b53_fdb_dump(struct dsa_switch *ds, + + /* Start search operation */ + reg = ARL_SRCH_STDN; +- b53_write8(priv, offset, B53_ARL_SRCH_CTL, reg); ++ b53_write8(priv, B53_ARLIO_PAGE, offset, reg); + + do { + ret = b53_arl_search_wait(priv); diff --git a/target/linux/generic/backport-6.12/611-17-v6.18-net-dsa-b53-fix-ageing-time-for-BCM53101.patch b/target/linux/generic/backport-6.12/611-17-v6.18-net-dsa-b53-fix-ageing-time-for-BCM53101.patch new file mode 100644 index 00000000000..0eb0870a4b5 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-17-v6.18-net-dsa-b53-fix-ageing-time-for-BCM53101.patch @@ -0,0 +1,77 @@ +From 674b34c4c770551e916ae707829c7faea4782d3a Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 5 Sep 2025 14:45:07 +0200 +Subject: [PATCH] net: dsa: b53: fix ageing time for BCM53101 + +For some reason Broadcom decided that BCM53101 uses 0.5s increments for +the ageing time register, but kept the field width the same [1]. Due to +this, the actual ageing time was always half of what was configured. + +Fix this by adapting the limits and value calculation for BCM53101. + +So far it looks like this is the only chip with the increased tick +speed: + +$ grep -l -r "Specifies the aging time in 0.5 seconds" cdk/PKG/chip | sort +cdk/PKG/chip/bcm53101/bcm53101_a0_defs.h + +$ grep -l -r "Specifies the aging time in seconds" cdk/PKG/chip | sort +cdk/PKG/chip/bcm53010/bcm53010_a0_defs.h +cdk/PKG/chip/bcm53020/bcm53020_a0_defs.h +cdk/PKG/chip/bcm53084/bcm53084_a0_defs.h +cdk/PKG/chip/bcm53115/bcm53115_a0_defs.h +cdk/PKG/chip/bcm53118/bcm53118_a0_defs.h +cdk/PKG/chip/bcm53125/bcm53125_a0_defs.h +cdk/PKG/chip/bcm53128/bcm53128_a0_defs.h +cdk/PKG/chip/bcm53134/bcm53134_a0_defs.h +cdk/PKG/chip/bcm53242/bcm53242_a0_defs.h +cdk/PKG/chip/bcm53262/bcm53262_a0_defs.h +cdk/PKG/chip/bcm53280/bcm53280_a0_defs.h +cdk/PKG/chip/bcm53280/bcm53280_b0_defs.h +cdk/PKG/chip/bcm53600/bcm53600_a0_defs.h +cdk/PKG/chip/bcm89500/bcm89500_a0_defs.h + +[1] https://github.com/Broadcom/OpenMDK/blob/a5d3fc9b12af3eeb68f2ca0ce7ec4056cd14d6c2/cdk/PKG/chip/bcm53101/bcm53101_a0_defs.h#L28966 + +Fixes: e39d14a760c0 ("net: dsa: b53: implement setting ageing time") +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20250905124507.59186-1-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1273,9 +1273,15 @@ static int b53_setup(struct dsa_switch * + */ + ds->untag_vlan_aware_bridge_pvid = true; + +- /* Ageing time is set in seconds */ +- ds->ageing_time_min = 1 * 1000; +- ds->ageing_time_max = AGE_TIME_MAX * 1000; ++ if (dev->chip_id == BCM53101_DEVICE_ID) { ++ /* BCM53101 uses 0.5 second increments */ ++ ds->ageing_time_min = 1 * 500; ++ ds->ageing_time_max = AGE_TIME_MAX * 500; ++ } else { ++ /* Everything else uses 1 second increments */ ++ ds->ageing_time_min = 1 * 1000; ++ ds->ageing_time_max = AGE_TIME_MAX * 1000; ++ } + + ret = b53_reset_switch(dev); + if (ret) { +@@ -2587,7 +2593,10 @@ int b53_set_ageing_time(struct dsa_switc + else + reg = B53_AGING_TIME_CONTROL; + +- atc = DIV_ROUND_CLOSEST(msecs, 1000); ++ if (dev->chip_id == BCM53101_DEVICE_ID) ++ atc = DIV_ROUND_CLOSEST(msecs, 500); ++ else ++ atc = DIV_ROUND_CLOSEST(msecs, 1000); + + if (!is5325(dev) && !is5365(dev)) + atc |= AGE_CHANGE; diff --git a/target/linux/generic/backport-6.12/611-18-v6.18-net-dsa-b53-properly-bound-ARL-searches-for-4-ARL-bin-chips.patch b/target/linux/generic/backport-6.12/611-18-v6.18-net-dsa-b53-properly-bound-ARL-searches-for-4-ARL-bin-chips.patch new file mode 100644 index 00000000000..50ddcf93ee7 --- /dev/null +++ b/target/linux/generic/backport-6.12/611-18-v6.18-net-dsa-b53-properly-bound-ARL-searches-for-4-ARL-bin-chips.patch @@ -0,0 +1,61 @@ +From e57723fe536f040cc2635ec1545dd0a7919a321e Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Sun, 2 Nov 2025 11:07:58 +0100 +Subject: [PATCH] net: dsa: b53: properly bound ARL searches for < 4 ARL bin + chips + +When iterating over the ARL table we stop at max ARL entries / 2, but +this is only valid if the chip actually returns 2 results at once. For +chips with only one result register we will stop before reaching the end +of the table if it is more than half full. + +Fix this by only dividing the maximum results by two if we have a chip +with more than one result register (i.e. those with 4 ARL bins). + +Fixes: cd169d799bee ("net: dsa: b53: Bound check ARL searches") +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251102100758.28352-4-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2090,13 +2090,16 @@ static int b53_fdb_copy(int port, const + int b53_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) + { ++ unsigned int count = 0, results_per_hit = 1; + struct b53_device *priv = ds->priv; + struct b53_arl_entry results[2]; +- unsigned int count = 0; + u8 offset; + int ret; + u8 reg; + ++ if (priv->num_arl_bins > 2) ++ results_per_hit = 2; ++ + mutex_lock(&priv->arl_mutex); + + if (is5325(priv) || is5365(priv)) +@@ -2118,7 +2121,7 @@ int b53_fdb_dump(struct dsa_switch *ds, + if (ret) + break; + +- if (priv->num_arl_bins > 2) { ++ if (results_per_hit == 2) { + b53_arl_search_rd(priv, 1, &results[1]); + ret = b53_fdb_copy(port, &results[1], cb, data); + if (ret) +@@ -2128,7 +2131,7 @@ int b53_fdb_dump(struct dsa_switch *ds, + break; + } + +- } while (count++ < b53_max_arl_entries(priv) / 2); ++ } while (count++ < b53_max_arl_entries(priv) / results_per_hit); + + mutex_unlock(&priv->arl_mutex); + diff --git a/target/linux/generic/backport-6.12/611-19-v6.18-net-dsa-tag_brcm-do-not-mark-link-local-traffic-as-offloaded.patch b/target/linux/generic/backport-6.12/611-19-v6.18-net-dsa-tag_brcm-do-not-mark-link-local-traffic-as-offloaded.patch new file mode 100644 index 00000000000..7c3d06269ab --- /dev/null +++ b/target/linux/generic/backport-6.12/611-19-v6.18-net-dsa-tag_brcm-do-not-mark-link-local-traffic-as-offloaded.patch @@ -0,0 +1,59 @@ +From 762e7e174da91cf4babfe77e45bc6b67334b1503 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Sun, 9 Nov 2025 14:46:35 +0100 +Subject: [PATCH] net: dsa: tag_brcm: do not mark link local traffic as + offloaded + +Broadcom switches locally terminate link local traffic and do not +forward it, so we should not mark it as offloaded. + +In some situations we still want/need to flood this traffic, e.g. if STP +is disabled, or it is explicitly enabled via the group_fwd_mask. But if +the skb is marked as offloaded, the kernel will assume this was already +done in hardware, and the packets never reach other bridge ports. + +So ensure that link local traffic is never marked as offloaded, so that +the kernel can forward/flood these packets in software if needed. + +Since the local termination in not configurable, check the destination +MAC, and never mark packets as offloaded if it is a link local ether +address. + +While modern switches set the tag reason code to BRCM_EG_RC_PROT_TERM +for trapped link local traffic, they also set it for link local traffic +that is flooded (01:80:c2:00:00:10 to 01:80:c2:00:00:2f), so we cannot +use it and need to look at the destination address for them as well. + +Fixes: 964dbf186eaa ("net: dsa: tag_brcm: add support for legacy tags") +Fixes: 0e62f543bed0 ("net: dsa: Fix duplicate frames flooded by learning") +Signed-off-by: Jonas Gorski +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251109134635.243951-1-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + net/dsa/tag_brcm.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/net/dsa/tag_brcm.c ++++ b/net/dsa/tag_brcm.c +@@ -176,7 +176,8 @@ static struct sk_buff *brcm_tag_rcv_ll(s + /* Remove Broadcom tag and update checksum */ + skb_pull_rcsum(skb, BRCM_TAG_LEN); + +- dsa_default_offload_fwd_mark(skb); ++ if (likely(!is_link_local_ether_addr(eth_hdr(skb)->h_dest))) ++ dsa_default_offload_fwd_mark(skb); + + return skb; + } +@@ -250,7 +251,8 @@ static struct sk_buff *brcm_leg_tag_rcv( + /* Remove Broadcom tag and update checksum */ + skb_pull_rcsum(skb, len); + +- dsa_default_offload_fwd_mark(skb); ++ if (likely(!is_link_local_ether_addr(eth_hdr(skb)->h_dest))) ++ dsa_default_offload_fwd_mark(skb); + + dsa_strip_etype_header(skb, len); + diff --git a/target/linux/generic/backport-6.12/612-01-v6.19-net-dsa-b53-b53_arl_read-25-use-the-entry-for-comparision.patch b/target/linux/generic/backport-6.12/612-01-v6.19-net-dsa-b53-b53_arl_read-25-use-the-entry-for-comparision.patch new file mode 100644 index 00000000000..2b6cff796f9 --- /dev/null +++ b/target/linux/generic/backport-6.12/612-01-v6.19-net-dsa-b53-b53_arl_read-25-use-the-entry-for-comparision.patch @@ -0,0 +1,85 @@ +From a6e4fd38bf2f2e2363b61c27f4e6c49b14e4bb07 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 7 Nov 2025 09:07:42 +0100 +Subject: [PATCH] net: dsa: b53: b53_arl_read{,25}(): use the entry for + comparision + +Align the b53_arl_read{,25}() functions by consistently using the +parsed arl entry instead of parsing the raw registers again. + +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251107080749.26936-2-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 22 ++++++++++------------ + 1 file changed, 10 insertions(+), 12 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1830,7 +1830,7 @@ static int b53_arl_rw_op(struct b53_devi + return b53_arl_op_wait(dev); + } + +-static int b53_arl_read(struct b53_device *dev, u64 mac, ++static int b53_arl_read(struct b53_device *dev, const u8 *mac, + u16 vid, struct b53_arl_entry *ent, u8 *idx) + { + DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES); +@@ -1854,14 +1854,13 @@ static int b53_arl_read(struct b53_devic + B53_ARLTBL_DATA_ENTRY(i), &fwd_entry); + b53_arl_to_entry(ent, mac_vid, fwd_entry); + +- if (!(fwd_entry & ARLTBL_VALID)) { ++ if (!ent->is_valid) { + set_bit(i, free_bins); + continue; + } +- if ((mac_vid & ARLTBL_MAC_MASK) != mac) ++ if (!ether_addr_equal(ent->mac, mac)) + continue; +- if (dev->vlan_enabled && +- ((mac_vid >> ARLTBL_VID_S) & ARLTBL_VID_MASK) != vid) ++ if (dev->vlan_enabled && ent->vid != vid) + continue; + *idx = i; + return 0; +@@ -1871,7 +1870,7 @@ static int b53_arl_read(struct b53_devic + return *idx >= dev->num_arl_bins ? -ENOSPC : -ENOENT; + } + +-static int b53_arl_read_25(struct b53_device *dev, u64 mac, ++static int b53_arl_read_25(struct b53_device *dev, const u8 *mac, + u16 vid, struct b53_arl_entry *ent, u8 *idx) + { + DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES); +@@ -1893,14 +1892,13 @@ static int b53_arl_read_25(struct b53_de + + b53_arl_to_entry_25(ent, mac_vid); + +- if (!(mac_vid & ARLTBL_VALID_25)) { ++ if (!ent->is_valid) { + set_bit(i, free_bins); + continue; + } +- if ((mac_vid & ARLTBL_MAC_MASK) != mac) ++ if (!ether_addr_equal(ent->mac, mac)) + continue; +- if (dev->vlan_enabled && +- ((mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25) != vid) ++ if (dev->vlan_enabled && ent->vid != vid) + continue; + *idx = i; + return 0; +@@ -1933,9 +1931,9 @@ static int b53_arl_op(struct b53_device + return ret; + + if (is5325(dev) || is5365(dev)) +- ret = b53_arl_read_25(dev, mac, vid, &ent, &idx); ++ ret = b53_arl_read_25(dev, addr, vid, &ent, &idx); + else +- ret = b53_arl_read(dev, mac, vid, &ent, &idx); ++ ret = b53_arl_read(dev, addr, vid, &ent, &idx); + + /* If this is a read, just finish now */ + if (op) diff --git a/target/linux/generic/backport-6.12/612-02-v6.19-net-dsa-b53-move-reading-ARL-entries-into-their-own-function.patch b/target/linux/generic/backport-6.12/612-02-v6.19-net-dsa-b53-move-reading-ARL-entries-into-their-own-function.patch new file mode 100644 index 00000000000..8cf85ce7adc --- /dev/null +++ b/target/linux/generic/backport-6.12/612-02-v6.19-net-dsa-b53-move-reading-ARL-entries-into-their-own-function.patch @@ -0,0 +1,117 @@ +From 4a291fe7226736a465ddb3fa93c21fcef7162ec7 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 7 Nov 2025 09:07:43 +0100 +Subject: [PATCH] net: dsa: b53: move reading ARL entries into their own + function + +Instead of duplicating the whole code iterating over all bins for +BCM5325, factor out reading and parsing the entry into its own +functions, and name it the modern one after the first chip with that ARL +format, (BCM53)95. + +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251107080749.26936-3-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 69 +++++++++++--------------------- + 1 file changed, 23 insertions(+), 46 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1830,48 +1830,30 @@ static int b53_arl_rw_op(struct b53_devi + return b53_arl_op_wait(dev); + } + +-static int b53_arl_read(struct b53_device *dev, const u8 *mac, +- u16 vid, struct b53_arl_entry *ent, u8 *idx) ++static void b53_arl_read_entry_25(struct b53_device *dev, ++ struct b53_arl_entry *ent, u8 idx) + { +- DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES); +- unsigned int i; +- int ret; +- +- ret = b53_arl_op_wait(dev); +- if (ret) +- return ret; ++ u64 mac_vid; + +- bitmap_zero(free_bins, dev->num_arl_bins); +- +- /* Read the bins */ +- for (i = 0; i < dev->num_arl_bins; i++) { +- u64 mac_vid; +- u32 fwd_entry; +- +- b53_read64(dev, B53_ARLIO_PAGE, +- B53_ARLTBL_MAC_VID_ENTRY(i), &mac_vid); +- b53_read32(dev, B53_ARLIO_PAGE, +- B53_ARLTBL_DATA_ENTRY(i), &fwd_entry); +- b53_arl_to_entry(ent, mac_vid, fwd_entry); ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx), ++ &mac_vid); ++ b53_arl_to_entry_25(ent, mac_vid); ++} + +- if (!ent->is_valid) { +- set_bit(i, free_bins); +- continue; +- } +- if (!ether_addr_equal(ent->mac, mac)) +- continue; +- if (dev->vlan_enabled && ent->vid != vid) +- continue; +- *idx = i; +- return 0; +- } ++static void b53_arl_read_entry_95(struct b53_device *dev, ++ struct b53_arl_entry *ent, u8 idx) ++{ ++ u32 fwd_entry; ++ u64 mac_vid; + +- *idx = find_first_bit(free_bins, dev->num_arl_bins); +- return *idx >= dev->num_arl_bins ? -ENOSPC : -ENOENT; ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx), ++ &mac_vid); ++ b53_read32(dev, B53_ARLIO_PAGE, B53_ARLTBL_DATA_ENTRY(idx), &fwd_entry); ++ b53_arl_to_entry(ent, mac_vid, fwd_entry); + } + +-static int b53_arl_read_25(struct b53_device *dev, const u8 *mac, +- u16 vid, struct b53_arl_entry *ent, u8 *idx) ++static int b53_arl_read(struct b53_device *dev, const u8 *mac, ++ u16 vid, struct b53_arl_entry *ent, u8 *idx) + { + DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES); + unsigned int i; +@@ -1885,12 +1867,10 @@ static int b53_arl_read_25(struct b53_de + + /* Read the bins */ + for (i = 0; i < dev->num_arl_bins; i++) { +- u64 mac_vid; +- +- b53_read64(dev, B53_ARLIO_PAGE, +- B53_ARLTBL_MAC_VID_ENTRY(i), &mac_vid); +- +- b53_arl_to_entry_25(ent, mac_vid); ++ if (is5325(dev) || is5365(dev)) ++ b53_arl_read_entry_25(dev, ent, i); ++ else ++ b53_arl_read_entry_95(dev, ent, i); + + if (!ent->is_valid) { + set_bit(i, free_bins); +@@ -1930,10 +1910,7 @@ static int b53_arl_op(struct b53_device + if (ret) + return ret; + +- if (is5325(dev) || is5365(dev)) +- ret = b53_arl_read_25(dev, addr, vid, &ent, &idx); +- else +- ret = b53_arl_read(dev, addr, vid, &ent, &idx); ++ ret = b53_arl_read(dev, addr, vid, &ent, &idx); + + /* If this is a read, just finish now */ + if (op) diff --git a/target/linux/generic/backport-6.12/612-03-v6.19-net-dsa-b53-move-writing-ARL-entries-into-their-own-functions.patch b/target/linux/generic/backport-6.12/612-03-v6.19-net-dsa-b53-move-writing-ARL-entries-into-their-own-functions.patch new file mode 100644 index 00000000000..a171232d49d --- /dev/null +++ b/target/linux/generic/backport-6.12/612-03-v6.19-net-dsa-b53-move-writing-ARL-entries-into-their-own-functions.patch @@ -0,0 +1,93 @@ +From bf6e9d2ae1dbafee53ec4ccd126595172e1e5278 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 7 Nov 2025 09:07:44 +0100 +Subject: [PATCH] net: dsa: b53: move writing ARL entries into their own + functions + +Move writing ARL entries into individual functions for each format. + +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251107080749.26936-4-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 38 ++++++++++++++++++++++---------- + 1 file changed, 26 insertions(+), 12 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1840,6 +1840,16 @@ static void b53_arl_read_entry_25(struct + b53_arl_to_entry_25(ent, mac_vid); + } + ++static void b53_arl_write_entry_25(struct b53_device *dev, ++ const struct b53_arl_entry *ent, u8 idx) ++{ ++ u64 mac_vid; ++ ++ b53_arl_from_entry_25(&mac_vid, ent); ++ b53_write64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx), ++ mac_vid); ++} ++ + static void b53_arl_read_entry_95(struct b53_device *dev, + struct b53_arl_entry *ent, u8 idx) + { +@@ -1852,6 +1862,19 @@ static void b53_arl_read_entry_95(struct + b53_arl_to_entry(ent, mac_vid, fwd_entry); + } + ++static void b53_arl_write_entry_95(struct b53_device *dev, ++ const struct b53_arl_entry *ent, u8 idx) ++{ ++ u32 fwd_entry; ++ u64 mac_vid; ++ ++ b53_arl_from_entry(&mac_vid, &fwd_entry, ent); ++ b53_write64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx), ++ mac_vid); ++ b53_write32(dev, B53_ARLIO_PAGE, B53_ARLTBL_DATA_ENTRY(idx), ++ fwd_entry); ++} ++ + static int b53_arl_read(struct b53_device *dev, const u8 *mac, + u16 vid, struct b53_arl_entry *ent, u8 *idx) + { +@@ -1892,9 +1915,8 @@ static int b53_arl_op(struct b53_device + const unsigned char *addr, u16 vid, bool is_valid) + { + struct b53_arl_entry ent; +- u32 fwd_entry; +- u64 mac, mac_vid = 0; + u8 idx = 0; ++ u64 mac; + int ret; + + /* Convert the array into a 64-bit MAC */ +@@ -1927,7 +1949,6 @@ static int b53_arl_op(struct b53_device + /* We could not find a matching MAC, so reset to a new entry */ + dev_dbg(dev->dev, "{%pM,%.4d} not found, using idx: %d\n", + addr, vid, idx); +- fwd_entry = 0; + break; + default: + dev_dbg(dev->dev, "{%pM,%.4d} found, using idx: %d\n", +@@ -1955,16 +1976,9 @@ static int b53_arl_op(struct b53_device + ent.is_age = false; + memcpy(ent.mac, addr, ETH_ALEN); + if (is5325(dev) || is5365(dev)) +- b53_arl_from_entry_25(&mac_vid, &ent); ++ b53_arl_write_entry_25(dev, &ent, idx); + else +- b53_arl_from_entry(&mac_vid, &fwd_entry, &ent); +- +- b53_write64(dev, B53_ARLIO_PAGE, +- B53_ARLTBL_MAC_VID_ENTRY(idx), mac_vid); +- +- if (!is5325(dev) && !is5365(dev)) +- b53_write32(dev, B53_ARLIO_PAGE, +- B53_ARLTBL_DATA_ENTRY(idx), fwd_entry); ++ b53_arl_write_entry_95(dev, &ent, idx); + + return b53_arl_rw_op(dev, 0); + } diff --git a/target/linux/generic/backport-6.12/612-04-v6.19-net-dsa-b53-provide-accessors-for-accessing-ARL_SRCH_CTL.patch b/target/linux/generic/backport-6.12/612-04-v6.19-net-dsa-b53-provide-accessors-for-accessing-ARL_SRCH_CTL.patch new file mode 100644 index 00000000000..2922427c3c7 --- /dev/null +++ b/target/linux/generic/backport-6.12/612-04-v6.19-net-dsa-b53-provide-accessors-for-accessing-ARL_SRCH_CTL.patch @@ -0,0 +1,85 @@ +From 1716be6db04af53bac9b869f01156a460595cf41 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 7 Nov 2025 09:07:45 +0100 +Subject: [PATCH] net: dsa: b53: provide accessors for accessing ARL_SRCH_CTL + +In order to more easily support more formats, move accessing +ARL_SRCH_CTL into helper functions to contain the differences. + +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251107080749.26936-5-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 37 +++++++++++++++++++++----------- + 1 file changed, 24 insertions(+), 13 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2013,18 +2013,37 @@ int b53_fdb_del(struct dsa_switch *ds, i + } + EXPORT_SYMBOL(b53_fdb_del); + +-static int b53_arl_search_wait(struct b53_device *dev) ++static void b53_read_arl_srch_ctl(struct b53_device *dev, u8 *val) + { +- unsigned int timeout = 1000; +- u8 reg, offset; ++ u8 offset; ++ ++ if (is5325(dev) || is5365(dev)) ++ offset = B53_ARL_SRCH_CTL_25; ++ else ++ offset = B53_ARL_SRCH_CTL; ++ ++ b53_read8(dev, B53_ARLIO_PAGE, offset, val); ++} ++ ++static void b53_write_arl_srch_ctl(struct b53_device *dev, u8 val) ++{ ++ u8 offset; + + if (is5325(dev) || is5365(dev)) + offset = B53_ARL_SRCH_CTL_25; + else + offset = B53_ARL_SRCH_CTL; + ++ b53_write8(dev, B53_ARLIO_PAGE, offset, val); ++} ++ ++static int b53_arl_search_wait(struct b53_device *dev) ++{ ++ unsigned int timeout = 1000; ++ u8 reg; ++ + do { +- b53_read8(dev, B53_ARLIO_PAGE, offset, ®); ++ b53_read_arl_srch_ctl(dev, ®); + if (!(reg & ARL_SRCH_STDN)) + return -ENOENT; + +@@ -2082,23 +2101,15 @@ int b53_fdb_dump(struct dsa_switch *ds, + unsigned int count = 0, results_per_hit = 1; + struct b53_device *priv = ds->priv; + struct b53_arl_entry results[2]; +- u8 offset; + int ret; +- u8 reg; + + if (priv->num_arl_bins > 2) + results_per_hit = 2; + + mutex_lock(&priv->arl_mutex); + +- if (is5325(priv) || is5365(priv)) +- offset = B53_ARL_SRCH_CTL_25; +- else +- offset = B53_ARL_SRCH_CTL; +- + /* Start search operation */ +- reg = ARL_SRCH_STDN; +- b53_write8(priv, B53_ARLIO_PAGE, offset, reg); ++ b53_write_arl_srch_ctl(priv, ARL_SRCH_STDN); + + do { + ret = b53_arl_search_wait(priv); diff --git a/target/linux/generic/backport-6.12/612-05-v6.19-net-dsa-b53-split-reading-search-entry-into-their-own-functions.patch b/target/linux/generic/backport-6.12/612-05-v6.19-net-dsa-b53-split-reading-search-entry-into-their-own-functions.patch new file mode 100644 index 00000000000..3e263f8cfbb --- /dev/null +++ b/target/linux/generic/backport-6.12/612-05-v6.19-net-dsa-b53-split-reading-search-entry-into-their-own-functions.patch @@ -0,0 +1,86 @@ +From e0c476f325a8c9b961a3d446c24d3c8ecae7d186 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 7 Nov 2025 09:07:46 +0100 +Subject: [PATCH] net: dsa: b53: split reading search entry into their own + functions + +Split reading search entries into a function for each format. + +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251107080749.26936-6-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 56 ++++++++++++++++++++++---------- + 1 file changed, 38 insertions(+), 18 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2056,28 +2056,48 @@ static int b53_arl_search_wait(struct b5 + return -ETIMEDOUT; + } + +-static void b53_arl_search_rd(struct b53_device *dev, u8 idx, +- struct b53_arl_entry *ent) ++static void b53_arl_search_read_25(struct b53_device *dev, u8 idx, ++ struct b53_arl_entry *ent) + { + u64 mac_vid; + +- if (is5325(dev)) { +- b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_25, +- &mac_vid); +- b53_arl_to_entry_25(ent, mac_vid); +- } else if (is5365(dev)) { +- b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_65, +- &mac_vid); +- b53_arl_to_entry_25(ent, mac_vid); +- } else { +- u32 fwd_entry; +- +- b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_MACVID(idx), +- &mac_vid); +- b53_read32(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL(idx), +- &fwd_entry); +- b53_arl_to_entry(ent, mac_vid, fwd_entry); +- } ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_25, ++ &mac_vid); ++ b53_arl_to_entry_25(ent, mac_vid); ++} ++ ++static void b53_arl_search_read_65(struct b53_device *dev, u8 idx, ++ struct b53_arl_entry *ent) ++{ ++ u64 mac_vid; ++ ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_65, ++ &mac_vid); ++ b53_arl_to_entry_25(ent, mac_vid); ++} ++ ++static void b53_arl_search_read_95(struct b53_device *dev, u8 idx, ++ struct b53_arl_entry *ent) ++{ ++ u32 fwd_entry; ++ u64 mac_vid; ++ ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_MACVID(idx), ++ &mac_vid); ++ b53_read32(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL(idx), ++ &fwd_entry); ++ b53_arl_to_entry(ent, mac_vid, fwd_entry); ++} ++ ++static void b53_arl_search_rd(struct b53_device *dev, u8 idx, ++ struct b53_arl_entry *ent) ++{ ++ if (is5325(dev)) ++ b53_arl_search_read_25(dev, idx, ent); ++ else if (is5365(dev)) ++ b53_arl_search_read_65(dev, idx, ent); ++ else ++ b53_arl_search_read_95(dev, idx, ent); + } + + static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, diff --git a/target/linux/generic/backport-6.12/612-06-v6.19-net-dsa-b53-move-ARL-entry-functions-into-ops-struct.patch b/target/linux/generic/backport-6.12/612-06-v6.19-net-dsa-b53-move-ARL-entry-functions-into-ops-struct.patch new file mode 100644 index 00000000000..110e160fed7 --- /dev/null +++ b/target/linux/generic/backport-6.12/612-06-v6.19-net-dsa-b53-move-ARL-entry-functions-into-ops-struct.patch @@ -0,0 +1,348 @@ +From a7e73339ad46ade76d29fb6cc7d7854222608c26 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 7 Nov 2025 09:07:47 +0100 +Subject: [PATCH] net: dsa: b53: move ARL entry functions into ops struct + +Now that the differences in ARL entry formats are neatly contained into +functions per chip family, wrap them into an ops struct and add wrapper +functions to access them. + +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251107080749.26936-7-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 67 ++++++++++++++++++++++---------- + drivers/net/dsa/b53/b53_priv.h | 30 ++++++++++++++ + 2 files changed, 76 insertions(+), 21 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1890,10 +1890,7 @@ static int b53_arl_read(struct b53_devic + + /* Read the bins */ + for (i = 0; i < dev->num_arl_bins; i++) { +- if (is5325(dev) || is5365(dev)) +- b53_arl_read_entry_25(dev, ent, i); +- else +- b53_arl_read_entry_95(dev, ent, i); ++ b53_arl_read_entry(dev, ent, i); + + if (!ent->is_valid) { + set_bit(i, free_bins); +@@ -1975,10 +1972,7 @@ static int b53_arl_op(struct b53_device + ent.is_static = true; + ent.is_age = false; + memcpy(ent.mac, addr, ETH_ALEN); +- if (is5325(dev) || is5365(dev)) +- b53_arl_write_entry_25(dev, &ent, idx); +- else +- b53_arl_write_entry_95(dev, &ent, idx); ++ b53_arl_write_entry(dev, &ent, idx); + + return b53_arl_rw_op(dev, 0); + } +@@ -2089,17 +2083,6 @@ static void b53_arl_search_read_95(struc + b53_arl_to_entry(ent, mac_vid, fwd_entry); + } + +-static void b53_arl_search_rd(struct b53_device *dev, u8 idx, +- struct b53_arl_entry *ent) +-{ +- if (is5325(dev)) +- b53_arl_search_read_25(dev, idx, ent); +- else if (is5365(dev)) +- b53_arl_search_read_65(dev, idx, ent); +- else +- b53_arl_search_read_95(dev, idx, ent); +-} +- + static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, + dsa_fdb_dump_cb_t *cb, void *data) + { +@@ -2136,13 +2119,13 @@ int b53_fdb_dump(struct dsa_switch *ds, + if (ret) + break; + +- b53_arl_search_rd(priv, 0, &results[0]); ++ b53_arl_search_read(priv, 0, &results[0]); + ret = b53_fdb_copy(port, &results[0], cb, data); + if (ret) + break; + + if (results_per_hit == 2) { +- b53_arl_search_rd(priv, 1, &results[1]); ++ b53_arl_search_read(priv, 1, &results[1]); + ret = b53_fdb_copy(port, &results[1], cb, data); + if (ret) + break; +@@ -2675,6 +2658,24 @@ static const struct dsa_switch_ops b53_s + .port_change_mtu = b53_change_mtu, + }; + ++static const struct b53_arl_ops b53_arl_ops_25 = { ++ .arl_read_entry = b53_arl_read_entry_25, ++ .arl_write_entry = b53_arl_write_entry_25, ++ .arl_search_read = b53_arl_search_read_25, ++}; ++ ++static const struct b53_arl_ops b53_arl_ops_65 = { ++ .arl_read_entry = b53_arl_read_entry_25, ++ .arl_write_entry = b53_arl_write_entry_25, ++ .arl_search_read = b53_arl_search_read_65, ++}; ++ ++static const struct b53_arl_ops b53_arl_ops_95 = { ++ .arl_read_entry = b53_arl_read_entry_95, ++ .arl_write_entry = b53_arl_write_entry_95, ++ .arl_search_read = b53_arl_search_read_95, ++}; ++ + struct b53_chip_data { + u32 chip_id; + const char *dev_name; +@@ -2688,6 +2689,7 @@ struct b53_chip_data { + u8 duplex_reg; + u8 jumbo_pm_reg; + u8 jumbo_size_reg; ++ const struct b53_arl_ops *arl_ops; + }; + + #define B53_VTA_REGS \ +@@ -2707,6 +2709,7 @@ static const struct b53_chip_data b53_sw + .arl_buckets = 1024, + .imp_port = 5, + .duplex_reg = B53_DUPLEX_STAT_FE, ++ .arl_ops = &b53_arl_ops_25, + }, + { + .chip_id = BCM5365_DEVICE_ID, +@@ -2717,6 +2720,7 @@ static const struct b53_chip_data b53_sw + .arl_buckets = 1024, + .imp_port = 5, + .duplex_reg = B53_DUPLEX_STAT_FE, ++ .arl_ops = &b53_arl_ops_65, + }, + { + .chip_id = BCM5389_DEVICE_ID, +@@ -2730,6 +2734,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM5395_DEVICE_ID, +@@ -2743,6 +2748,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM5397_DEVICE_ID, +@@ -2756,6 +2762,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM5398_DEVICE_ID, +@@ -2769,6 +2776,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM53101_DEVICE_ID, +@@ -2782,6 +2790,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM53115_DEVICE_ID, +@@ -2795,6 +2804,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM53125_DEVICE_ID, +@@ -2808,6 +2818,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM53128_DEVICE_ID, +@@ -2821,6 +2832,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM63XX_DEVICE_ID, +@@ -2834,6 +2846,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_63XX, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM53010_DEVICE_ID, +@@ -2847,6 +2860,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM53011_DEVICE_ID, +@@ -2860,6 +2874,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM53012_DEVICE_ID, +@@ -2873,6 +2888,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM53018_DEVICE_ID, +@@ -2886,6 +2902,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM53019_DEVICE_ID, +@@ -2899,6 +2916,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM58XX_DEVICE_ID, +@@ -2912,6 +2930,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM583XX_DEVICE_ID, +@@ -2925,6 +2944,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + /* Starfighter 2 */ + { +@@ -2939,6 +2959,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM7445_DEVICE_ID, +@@ -2952,6 +2973,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM7278_DEVICE_ID, +@@ -2965,6 +2987,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + { + .chip_id = BCM53134_DEVICE_ID, +@@ -2979,6 +3002,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ .arl_ops = &b53_arl_ops_95, + }, + }; + +@@ -3007,6 +3031,7 @@ static int b53_switch_init(struct b53_de + dev->num_vlans = chip->vlans; + dev->num_arl_bins = chip->arl_bins; + dev->num_arl_buckets = chip->arl_buckets; ++ dev->arl_ops = chip->arl_ops; + break; + } + } +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -58,6 +58,17 @@ struct b53_io_ops { + bool link_up); + }; + ++struct b53_arl_entry; ++ ++struct b53_arl_ops { ++ void (*arl_read_entry)(struct b53_device *dev, ++ struct b53_arl_entry *ent, u8 idx); ++ void (*arl_write_entry)(struct b53_device *dev, ++ const struct b53_arl_entry *ent, u8 idx); ++ void (*arl_search_read)(struct b53_device *dev, u8 idx, ++ struct b53_arl_entry *ent); ++}; ++ + #define B53_INVALID_LANE 0xff + + enum { +@@ -127,6 +138,7 @@ struct b53_device { + struct mutex stats_mutex; + struct mutex arl_mutex; + const struct b53_io_ops *ops; ++ const struct b53_arl_ops *arl_ops; + + /* chip specific data */ + u32 chip_id; +@@ -371,6 +383,24 @@ static inline void b53_arl_from_entry_25 + *mac_vid |= ARLTBL_AGE_25; + } + ++static inline void b53_arl_read_entry(struct b53_device *dev, ++ struct b53_arl_entry *ent, u8 idx) ++{ ++ dev->arl_ops->arl_read_entry(dev, ent, idx); ++} ++ ++static inline void b53_arl_write_entry(struct b53_device *dev, ++ const struct b53_arl_entry *ent, u8 idx) ++{ ++ dev->arl_ops->arl_write_entry(dev, ent, idx); ++} ++ ++static inline void b53_arl_search_read(struct b53_device *dev, u8 idx, ++ struct b53_arl_entry *ent) ++{ ++ dev->arl_ops->arl_search_read(dev, idx, ent); ++} ++ + #ifdef CONFIG_BCM47XX + + #include diff --git a/target/linux/generic/backport-6.12/612-07-v6.19-net-dsa-b53-add-support-for-5389-5397-5398-ARL-entry-format.patch b/target/linux/generic/backport-6.12/612-07-v6.19-net-dsa-b53-add-support-for-5389-5397-5398-ARL-entry-format.patch new file mode 100644 index 00000000000..948b4ab67b0 --- /dev/null +++ b/target/linux/generic/backport-6.12/612-07-v6.19-net-dsa-b53-add-support-for-5389-5397-5398-ARL-entry-format.patch @@ -0,0 +1,221 @@ +From 300f78e8b6b7be17c2c78afeded75be68acb1aa7 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 7 Nov 2025 09:07:48 +0100 +Subject: [PATCH] net: dsa: b53: add support for 5389/5397/5398 ARL entry + format + +BCM5389, BCM5397 and BCM5398 use a different ARL entry format with just +a 16 bit fwdentry register, as well as different search control and data +offsets. + +So add appropriate ops for them and switch those chips to use them. + +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251107080749.26936-8-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 53 ++++++++++++++++++++++++++++++-- + drivers/net/dsa/b53/b53_priv.h | 26 ++++++++++++++++ + drivers/net/dsa/b53/b53_regs.h | 13 ++++++++ + 3 files changed, 89 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1850,6 +1850,31 @@ static void b53_arl_write_entry_25(struc + mac_vid); + } + ++static void b53_arl_read_entry_89(struct b53_device *dev, ++ struct b53_arl_entry *ent, u8 idx) ++{ ++ u64 mac_vid; ++ u16 fwd_entry; ++ ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx), ++ &mac_vid); ++ b53_read16(dev, B53_ARLIO_PAGE, B53_ARLTBL_DATA_ENTRY(idx), &fwd_entry); ++ b53_arl_to_entry_89(ent, mac_vid, fwd_entry); ++} ++ ++static void b53_arl_write_entry_89(struct b53_device *dev, ++ const struct b53_arl_entry *ent, u8 idx) ++{ ++ u32 fwd_entry; ++ u64 mac_vid; ++ ++ b53_arl_from_entry_89(&mac_vid, &fwd_entry, ent); ++ b53_write64(dev, B53_ARLIO_PAGE, ++ B53_ARLTBL_MAC_VID_ENTRY(idx), mac_vid); ++ b53_write16(dev, B53_ARLIO_PAGE, ++ B53_ARLTBL_DATA_ENTRY(idx), fwd_entry); ++} ++ + static void b53_arl_read_entry_95(struct b53_device *dev, + struct b53_arl_entry *ent, u8 idx) + { +@@ -2013,6 +2038,8 @@ static void b53_read_arl_srch_ctl(struct + + if (is5325(dev) || is5365(dev)) + offset = B53_ARL_SRCH_CTL_25; ++ else if (dev->chip_id == BCM5389_DEVICE_ID || is5397_98(dev)) ++ offset = B53_ARL_SRCH_CTL_89; + else + offset = B53_ARL_SRCH_CTL; + +@@ -2025,6 +2052,8 @@ static void b53_write_arl_srch_ctl(struc + + if (is5325(dev) || is5365(dev)) + offset = B53_ARL_SRCH_CTL_25; ++ else if (dev->chip_id == BCM5389_DEVICE_ID || is5397_98(dev)) ++ offset = B53_ARL_SRCH_CTL_89; + else + offset = B53_ARL_SRCH_CTL; + +@@ -2070,6 +2099,18 @@ static void b53_arl_search_read_65(struc + b53_arl_to_entry_25(ent, mac_vid); + } + ++static void b53_arl_search_read_89(struct b53_device *dev, u8 idx, ++ struct b53_arl_entry *ent) ++{ ++ u16 fwd_entry; ++ u64 mac_vid; ++ ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSLT_MACVID_89, ++ &mac_vid); ++ b53_read16(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSLT_89, &fwd_entry); ++ b53_arl_to_entry_89(ent, mac_vid, fwd_entry); ++} ++ + static void b53_arl_search_read_95(struct b53_device *dev, u8 idx, + struct b53_arl_entry *ent) + { +@@ -2670,6 +2711,12 @@ static const struct b53_arl_ops b53_arl_ + .arl_search_read = b53_arl_search_read_65, + }; + ++static const struct b53_arl_ops b53_arl_ops_89 = { ++ .arl_read_entry = b53_arl_read_entry_89, ++ .arl_write_entry = b53_arl_write_entry_89, ++ .arl_search_read = b53_arl_search_read_89, ++}; ++ + static const struct b53_arl_ops b53_arl_ops_95 = { + .arl_read_entry = b53_arl_read_entry_95, + .arl_write_entry = b53_arl_write_entry_95, +@@ -2734,7 +2781,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, +- .arl_ops = &b53_arl_ops_95, ++ .arl_ops = &b53_arl_ops_89, + }, + { + .chip_id = BCM5395_DEVICE_ID, +@@ -2762,7 +2809,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, +- .arl_ops = &b53_arl_ops_95, ++ .arl_ops = &b53_arl_ops_89, + }, + { + .chip_id = BCM5398_DEVICE_ID, +@@ -2776,7 +2823,7 @@ static const struct b53_chip_data b53_sw + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, +- .arl_ops = &b53_arl_ops_95, ++ .arl_ops = &b53_arl_ops_89, + }, + { + .chip_id = BCM53101_DEVICE_ID, +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -353,6 +353,18 @@ static inline void b53_arl_to_entry_25(s + ent->vid = mac_vid >> ARLTBL_VID_S_65; + } + ++static inline void b53_arl_to_entry_89(struct b53_arl_entry *ent, ++ u64 mac_vid, u16 fwd_entry) ++{ ++ memset(ent, 0, sizeof(*ent)); ++ ent->port = fwd_entry & ARLTBL_DATA_PORT_ID_MASK_89; ++ ent->is_valid = !!(fwd_entry & ARLTBL_VALID_89); ++ ent->is_age = !!(fwd_entry & ARLTBL_AGE_89); ++ ent->is_static = !!(fwd_entry & ARLTBL_STATIC_89); ++ u64_to_ether_addr(mac_vid, ent->mac); ++ ent->vid = mac_vid >> ARLTBL_VID_S; ++} ++ + static inline void b53_arl_from_entry(u64 *mac_vid, u32 *fwd_entry, + const struct b53_arl_entry *ent) + { +@@ -383,6 +395,20 @@ static inline void b53_arl_from_entry_25 + *mac_vid |= ARLTBL_AGE_25; + } + ++static inline void b53_arl_from_entry_89(u64 *mac_vid, u32 *fwd_entry, ++ const struct b53_arl_entry *ent) ++{ ++ *mac_vid = ether_addr_to_u64(ent->mac); ++ *mac_vid |= (u64)(ent->vid & ARLTBL_VID_MASK) << ARLTBL_VID_S; ++ *fwd_entry = ent->port & ARLTBL_DATA_PORT_ID_MASK_89; ++ if (ent->is_valid) ++ *fwd_entry |= ARLTBL_VALID_89; ++ if (ent->is_static) ++ *fwd_entry |= ARLTBL_STATIC_89; ++ if (ent->is_age) ++ *fwd_entry |= ARLTBL_AGE_89; ++} ++ + static inline void b53_arl_read_entry(struct b53_device *dev, + struct b53_arl_entry *ent, u8 idx) + { +--- a/drivers/net/dsa/b53/b53_regs.h ++++ b/drivers/net/dsa/b53/b53_regs.h +@@ -342,12 +342,20 @@ + #define ARLTBL_STATIC BIT(15) + #define ARLTBL_VALID BIT(16) + ++/* BCM5389 ARL Table Data Entry N Register format (16 bit) */ ++#define ARLTBL_DATA_PORT_ID_MASK_89 GENMASK(8, 0) ++#define ARLTBL_TC_MASK_89 GENMASK(12, 10) ++#define ARLTBL_AGE_89 BIT(13) ++#define ARLTBL_STATIC_89 BIT(14) ++#define ARLTBL_VALID_89 BIT(15) ++ + /* Maximum number of bin entries in the ARL for all switches */ + #define B53_ARLTBL_MAX_BIN_ENTRIES 4 + + /* ARL Search Control Register (8 bit) */ + #define B53_ARL_SRCH_CTL 0x50 + #define B53_ARL_SRCH_CTL_25 0x20 ++#define B53_ARL_SRCH_CTL_89 0x30 + #define ARL_SRCH_VLID BIT(0) + #define ARL_SRCH_STDN BIT(7) + +@@ -355,10 +363,12 @@ + #define B53_ARL_SRCH_ADDR 0x51 + #define B53_ARL_SRCH_ADDR_25 0x22 + #define B53_ARL_SRCH_ADDR_65 0x24 ++#define B53_ARL_SRCH_ADDR_89 0x31 + #define ARL_ADDR_MASK GENMASK(14, 0) + + /* ARL Search MAC/VID Result (64 bit) */ + #define B53_ARL_SRCH_RSTL_0_MACVID 0x60 ++#define B53_ARL_SRCH_RSLT_MACVID_89 0x33 + + /* Single register search result on 5325 */ + #define B53_ARL_SRCH_RSTL_0_MACVID_25 0x24 +@@ -368,6 +378,9 @@ + /* ARL Search Data Result (32 bit) */ + #define B53_ARL_SRCH_RSTL_0 0x68 + ++/* BCM5389 ARL Search Data Result (16 bit) */ ++#define B53_ARL_SRCH_RSLT_89 0x3b ++ + #define B53_ARL_SRCH_RSTL_MACVID(x) (B53_ARL_SRCH_RSTL_0_MACVID + ((x) * 0x10)) + #define B53_ARL_SRCH_RSTL(x) (B53_ARL_SRCH_RSTL_0 + ((x) * 0x10)) + diff --git a/target/linux/generic/backport-6.12/612-08-v6.19-net-dsa-b53-add-support-for-bcm63xx-ARL-entry-format.patch b/target/linux/generic/backport-6.12/612-08-v6.19-net-dsa-b53-add-support-for-bcm63xx-ARL-entry-format.patch new file mode 100644 index 00000000000..ec0e85e4cf5 --- /dev/null +++ b/target/linux/generic/backport-6.12/612-08-v6.19-net-dsa-b53-add-support-for-bcm63xx-ARL-entry-format.patch @@ -0,0 +1,177 @@ +From 2b3013ac03028a2364d8779719bb6bfbc0212435 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 7 Nov 2025 09:07:49 +0100 +Subject: [PATCH] net: dsa: b53: add support for bcm63xx ARL entry format + +The ARL registers of BCM63XX embedded switches are somewhat unique. The +normal ARL table access registers have the same format as BCM5389, but +the ARL search registers differ: + +* SRCH_CTL is at the same offset of BCM5389, but 16 bits wide. It does + not have more fields, just needs to be accessed by a 16 bit read. +* SRCH_RSLT_MACVID and SRCH_RSLT are aligned to 32 bit, and have shifted + offsets. +* SRCH_RSLT has a different format than the normal ARL data entry + register. +* There is only one set of ENTRY_N registers, implying a 1 bin layout. + +So add appropriate ops for bcm63xx and let it use it. + +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251107080749.26936-9-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 44 +++++++++++++++++++++++++++----- + drivers/net/dsa/b53/b53_priv.h | 15 +++++++++++ + drivers/net/dsa/b53/b53_regs.h | 9 +++++++ + 3 files changed, 61 insertions(+), 7 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2038,12 +2038,20 @@ static void b53_read_arl_srch_ctl(struct + + if (is5325(dev) || is5365(dev)) + offset = B53_ARL_SRCH_CTL_25; +- else if (dev->chip_id == BCM5389_DEVICE_ID || is5397_98(dev)) ++ else if (dev->chip_id == BCM5389_DEVICE_ID || is5397_98(dev) || ++ is63xx(dev)) + offset = B53_ARL_SRCH_CTL_89; + else + offset = B53_ARL_SRCH_CTL; + +- b53_read8(dev, B53_ARLIO_PAGE, offset, val); ++ if (is63xx(dev)) { ++ u16 val16; ++ ++ b53_read16(dev, B53_ARLIO_PAGE, offset, &val16); ++ *val = val16 & 0xff; ++ } else { ++ b53_read8(dev, B53_ARLIO_PAGE, offset, val); ++ } + } + + static void b53_write_arl_srch_ctl(struct b53_device *dev, u8 val) +@@ -2052,12 +2060,16 @@ static void b53_write_arl_srch_ctl(struc + + if (is5325(dev) || is5365(dev)) + offset = B53_ARL_SRCH_CTL_25; +- else if (dev->chip_id == BCM5389_DEVICE_ID || is5397_98(dev)) ++ else if (dev->chip_id == BCM5389_DEVICE_ID || is5397_98(dev) || ++ is63xx(dev)) + offset = B53_ARL_SRCH_CTL_89; + else + offset = B53_ARL_SRCH_CTL; + +- b53_write8(dev, B53_ARLIO_PAGE, offset, val); ++ if (is63xx(dev)) ++ b53_write16(dev, B53_ARLIO_PAGE, offset, val); ++ else ++ b53_write8(dev, B53_ARLIO_PAGE, offset, val); + } + + static int b53_arl_search_wait(struct b53_device *dev) +@@ -2111,6 +2123,18 @@ static void b53_arl_search_read_89(struc + b53_arl_to_entry_89(ent, mac_vid, fwd_entry); + } + ++static void b53_arl_search_read_63xx(struct b53_device *dev, u8 idx, ++ struct b53_arl_entry *ent) ++{ ++ u16 fwd_entry; ++ u64 mac_vid; ++ ++ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSLT_MACVID_63XX, ++ &mac_vid); ++ b53_read16(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSLT_63XX, &fwd_entry); ++ b53_arl_search_to_entry_63xx(ent, mac_vid, fwd_entry); ++} ++ + static void b53_arl_search_read_95(struct b53_device *dev, u8 idx, + struct b53_arl_entry *ent) + { +@@ -2717,6 +2741,12 @@ static const struct b53_arl_ops b53_arl_ + .arl_search_read = b53_arl_search_read_89, + }; + ++static const struct b53_arl_ops b53_arl_ops_63xx = { ++ .arl_read_entry = b53_arl_read_entry_89, ++ .arl_write_entry = b53_arl_write_entry_89, ++ .arl_search_read = b53_arl_search_read_63xx, ++}; ++ + static const struct b53_arl_ops b53_arl_ops_95 = { + .arl_read_entry = b53_arl_read_entry_95, + .arl_write_entry = b53_arl_write_entry_95, +@@ -2886,14 +2916,14 @@ static const struct b53_chip_data b53_sw + .dev_name = "BCM63xx", + .vlans = 4096, + .enabled_ports = 0, /* pdata must provide them */ +- .arl_bins = 4, +- .arl_buckets = 1024, ++ .arl_bins = 1, ++ .arl_buckets = 4096, + .imp_port = 8, + .vta_regs = B53_VTA_REGS_63XX, + .duplex_reg = B53_DUPLEX_STAT_63XX, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, +- .arl_ops = &b53_arl_ops_95, ++ .arl_ops = &b53_arl_ops_63xx, + }, + { + .chip_id = BCM53010_DEVICE_ID, +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -409,6 +409,21 @@ static inline void b53_arl_from_entry_89 + *fwd_entry |= ARLTBL_AGE_89; + } + ++static inline void b53_arl_search_to_entry_63xx(struct b53_arl_entry *ent, ++ u64 mac_vid, u16 fwd_entry) ++{ ++ memset(ent, 0, sizeof(*ent)); ++ u64_to_ether_addr(mac_vid, ent->mac); ++ ent->vid = mac_vid >> ARLTBL_VID_S; ++ ++ ent->port = fwd_entry & ARL_SRST_PORT_ID_MASK_63XX; ++ ent->port >>= 1; ++ ++ ent->is_age = !!(fwd_entry & ARL_SRST_AGE_63XX); ++ ent->is_static = !!(fwd_entry & ARL_SRST_STATIC_63XX); ++ ent->is_valid = 1; ++} ++ + static inline void b53_arl_read_entry(struct b53_device *dev, + struct b53_arl_entry *ent, u8 idx) + { +--- a/drivers/net/dsa/b53/b53_regs.h ++++ b/drivers/net/dsa/b53/b53_regs.h +@@ -364,11 +364,13 @@ + #define B53_ARL_SRCH_ADDR_25 0x22 + #define B53_ARL_SRCH_ADDR_65 0x24 + #define B53_ARL_SRCH_ADDR_89 0x31 ++#define B53_ARL_SRCH_ADDR_63XX 0x32 + #define ARL_ADDR_MASK GENMASK(14, 0) + + /* ARL Search MAC/VID Result (64 bit) */ + #define B53_ARL_SRCH_RSTL_0_MACVID 0x60 + #define B53_ARL_SRCH_RSLT_MACVID_89 0x33 ++#define B53_ARL_SRCH_RSLT_MACVID_63XX 0x34 + + /* Single register search result on 5325 */ + #define B53_ARL_SRCH_RSTL_0_MACVID_25 0x24 +@@ -384,6 +386,13 @@ + #define B53_ARL_SRCH_RSTL_MACVID(x) (B53_ARL_SRCH_RSTL_0_MACVID + ((x) * 0x10)) + #define B53_ARL_SRCH_RSTL(x) (B53_ARL_SRCH_RSTL_0 + ((x) * 0x10)) + ++/* 63XX ARL Search Data Result (16 bit) */ ++#define B53_ARL_SRCH_RSLT_63XX 0x3c ++#define ARL_SRST_PORT_ID_MASK_63XX GENMASK(9, 1) ++#define ARL_SRST_TC_MASK_63XX GENMASK(13, 11) ++#define ARL_SRST_AGE_63XX BIT(14) ++#define ARL_SRST_STATIC_63XX BIT(15) ++ + /************************************************************************* + * IEEE 802.1X Registers + *************************************************************************/ diff --git a/target/linux/generic/backport-6.12/613-01-v6.19-net-dsa-b53-fix-VLAN_ID_IDX-write-size-for-BCM5325-65.patch b/target/linux/generic/backport-6.12/613-01-v6.19-net-dsa-b53-fix-VLAN_ID_IDX-write-size-for-BCM5325-65.patch new file mode 100644 index 00000000000..d953ea7545b --- /dev/null +++ b/target/linux/generic/backport-6.12/613-01-v6.19-net-dsa-b53-fix-VLAN_ID_IDX-write-size-for-BCM5325-65.patch @@ -0,0 +1,38 @@ +From 6f268e275c74dae0536e0b61982a8db25bcf4f16 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 28 Nov 2025 09:06:19 +0100 +Subject: [PATCH] net: dsa: b53: fix VLAN_ID_IDX write size for BCM5325/65 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Since BCM5325 and BCM5365 only support up to 256 VLANs, the VLAN_ID_IDX +register is only 8 bit wide, not 16 bit, so use an appropriate accessor. + +Fixes: c45655386e53 ("net: dsa: b53: add support for FDB operations on 5325/5365") +Reviewed-by: Florian Fainelli +Tested-by: Álvaro Fernández Rojas +Signed-off-by: Jonas Gorski +Link: https://patch.msgid.link/20251128080625.27181-2-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1946,8 +1946,12 @@ static int b53_arl_op(struct b53_device + + /* Perform a read for the given MAC and VID */ + b53_write48(dev, B53_ARLIO_PAGE, B53_MAC_ADDR_IDX, mac); +- if (!is5325m(dev)) +- b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid); ++ if (!is5325m(dev)) { ++ if (is5325(dev) || is5365(dev)) ++ b53_write8(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid); ++ else ++ b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid); ++ } + + /* Issue a read operation for this MAC */ + ret = b53_arl_rw_op(dev, 1); diff --git a/target/linux/generic/backport-6.12/613-02-v6.19-net-dsa-b53-fix-extracting-VID-from-entry-for-BCM5325-65.patch b/target/linux/generic/backport-6.12/613-02-v6.19-net-dsa-b53-fix-extracting-VID-from-entry-for-BCM5325-65.patch new file mode 100644 index 00000000000..72f81d020f8 --- /dev/null +++ b/target/linux/generic/backport-6.12/613-02-v6.19-net-dsa-b53-fix-extracting-VID-from-entry-for-BCM5325-65.patch @@ -0,0 +1,35 @@ +From 9316012dd01952f75e37035360138ccc786ef727 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 28 Nov 2025 09:06:20 +0100 +Subject: [PATCH] net: dsa: b53: fix extracting VID from entry for BCM5325/65 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BCM5325/65's Entry register uses the highest three bits for +VALID/STATIC/AGE, so shifting by 53 only will add these to +b53_arl_entry::vid. + +So make sure to mask the vid value as well, to not get invalid VIDs. + +Fixes: c45655386e53 ("net: dsa: b53: add support for FDB operations on 5325/5365") +Reviewed-by: Florian Fainelli +Tested-by: Álvaro Fernández Rojas +Signed-off-by: Jonas Gorski +Link: https://patch.msgid.link/20251128080625.27181-3-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_priv.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -350,7 +350,7 @@ static inline void b53_arl_to_entry_25(s + ent->is_age = !!(mac_vid & ARLTBL_AGE_25); + ent->is_static = !!(mac_vid & ARLTBL_STATIC_25); + u64_to_ether_addr(mac_vid, ent->mac); +- ent->vid = mac_vid >> ARLTBL_VID_S_65; ++ ent->vid = (mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25; + } + + static inline void b53_arl_to_entry_89(struct b53_arl_entry *ent, diff --git a/target/linux/generic/backport-6.12/613-03-v6.19-net-dsa-b53-use-same-ARL-search-result-offset-for-BCM5325-65.patch b/target/linux/generic/backport-6.12/613-03-v6.19-net-dsa-b53-use-same-ARL-search-result-offset-for-BCM5325-65.patch new file mode 100644 index 00000000000..ddb95dfcdc5 --- /dev/null +++ b/target/linux/generic/backport-6.12/613-03-v6.19-net-dsa-b53-use-same-ARL-search-result-offset-for-BCM5325-65.patch @@ -0,0 +1,78 @@ +From 8e46aacea4264bcb8d4265fb07577afff58ae78d Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 28 Nov 2025 09:06:21 +0100 +Subject: [PATCH] net: dsa: b53: use same ARL search result offset for BCM5325/65 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BCM5365's search result is at the same offset as BCM5325's search +result, and they (mostly) share the same format, so switch BCM5365 to +BCM5325's arl ops. + +Fixes: c45655386e53 ("net: dsa: b53: add support for FDB operations on 5325/5365") +Reviewed-by: Florian Fainelli +Tested-by: Álvaro Fernández Rojas +Signed-off-by: Jonas Gorski +Link: https://patch.msgid.link/20251128080625.27181-4-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 18 +----------------- + drivers/net/dsa/b53/b53_regs.h | 4 +--- + 2 files changed, 2 insertions(+), 20 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2105,16 +2105,6 @@ static void b53_arl_search_read_25(struc + b53_arl_to_entry_25(ent, mac_vid); + } + +-static void b53_arl_search_read_65(struct b53_device *dev, u8 idx, +- struct b53_arl_entry *ent) +-{ +- u64 mac_vid; +- +- b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_65, +- &mac_vid); +- b53_arl_to_entry_25(ent, mac_vid); +-} +- + static void b53_arl_search_read_89(struct b53_device *dev, u8 idx, + struct b53_arl_entry *ent) + { +@@ -2733,12 +2723,6 @@ static const struct b53_arl_ops b53_arl_ + .arl_search_read = b53_arl_search_read_25, + }; + +-static const struct b53_arl_ops b53_arl_ops_65 = { +- .arl_read_entry = b53_arl_read_entry_25, +- .arl_write_entry = b53_arl_write_entry_25, +- .arl_search_read = b53_arl_search_read_65, +-}; +- + static const struct b53_arl_ops b53_arl_ops_89 = { + .arl_read_entry = b53_arl_read_entry_89, + .arl_write_entry = b53_arl_write_entry_89, +@@ -2801,7 +2785,7 @@ static const struct b53_chip_data b53_sw + .arl_buckets = 1024, + .imp_port = 5, + .duplex_reg = B53_DUPLEX_STAT_FE, +- .arl_ops = &b53_arl_ops_65, ++ .arl_ops = &b53_arl_ops_25, + }, + { + .chip_id = BCM5389_DEVICE_ID, +--- a/drivers/net/dsa/b53/b53_regs.h ++++ b/drivers/net/dsa/b53/b53_regs.h +@@ -372,10 +372,8 @@ + #define B53_ARL_SRCH_RSLT_MACVID_89 0x33 + #define B53_ARL_SRCH_RSLT_MACVID_63XX 0x34 + +-/* Single register search result on 5325 */ ++/* Single register search result on 5325/5365 */ + #define B53_ARL_SRCH_RSTL_0_MACVID_25 0x24 +-/* Single register search result on 5365 */ +-#define B53_ARL_SRCH_RSTL_0_MACVID_65 0x30 + + /* ARL Search Data Result (32 bit) */ + #define B53_ARL_SRCH_RSTL_0 0x68 diff --git a/target/linux/generic/backport-6.12/613-04-v6.19-net-dsa-b53-fix-CPU-port-unicast-ARL-entries-for-BCM5325-65.patch b/target/linux/generic/backport-6.12/613-04-v6.19-net-dsa-b53-fix-CPU-port-unicast-ARL-entries-for-BCM5325-65.patch new file mode 100644 index 00000000000..3748892ca8d --- /dev/null +++ b/target/linux/generic/backport-6.12/613-04-v6.19-net-dsa-b53-fix-CPU-port-unicast-ARL-entries-for-BCM5325-65.patch @@ -0,0 +1,51 @@ +From 85132103f700b1340fc17df8a981509d17bf4872 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 28 Nov 2025 09:06:22 +0100 +Subject: [PATCH] net: dsa: b53: fix CPU port unicast ARL entries for BCM5325/65 + +On BCM5325 and BCM5365, unicast ARL entries use 8 as the value for the +CPU port, so we need to translate it to/from 5 as used for the CPU port +at most other places. + +Fixes: c45655386e53 ("net: dsa: b53: add support for FDB operations on 5325/5365") +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251128080625.27181-5-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_priv.h | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -344,12 +344,14 @@ static inline void b53_arl_to_entry_25(s + u64 mac_vid) + { + memset(ent, 0, sizeof(*ent)); +- ent->port = (mac_vid >> ARLTBL_DATA_PORT_ID_S_25) & +- ARLTBL_DATA_PORT_ID_MASK_25; + ent->is_valid = !!(mac_vid & ARLTBL_VALID_25); + ent->is_age = !!(mac_vid & ARLTBL_AGE_25); + ent->is_static = !!(mac_vid & ARLTBL_STATIC_25); + u64_to_ether_addr(mac_vid, ent->mac); ++ ent->port = (mac_vid >> ARLTBL_DATA_PORT_ID_S_25) & ++ ARLTBL_DATA_PORT_ID_MASK_25; ++ if (is_unicast_ether_addr(ent->mac) && ent->port == B53_CPU_PORT) ++ ent->port = B53_CPU_PORT_25; + ent->vid = (mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25; + } + +@@ -383,8 +385,11 @@ static inline void b53_arl_from_entry_25 + const struct b53_arl_entry *ent) + { + *mac_vid = ether_addr_to_u64(ent->mac); +- *mac_vid |= (u64)(ent->port & ARLTBL_DATA_PORT_ID_MASK_25) << +- ARLTBL_DATA_PORT_ID_S_25; ++ if (is_unicast_ether_addr(ent->mac) && ent->port == B53_CPU_PORT_25) ++ *mac_vid |= (u64)B53_CPU_PORT << ARLTBL_DATA_PORT_ID_S_25; ++ else ++ *mac_vid |= (u64)(ent->port & ARLTBL_DATA_PORT_ID_MASK_25) << ++ ARLTBL_DATA_PORT_ID_S_25; + *mac_vid |= (u64)(ent->vid & ARLTBL_VID_MASK_25) << + ARLTBL_VID_S_65; + if (ent->is_valid) diff --git a/target/linux/generic/backport-6.12/613-05-v6.19-net-dsa-b53-fix-BCM5325-65-ARL-entry-multicast-port-masks.patch b/target/linux/generic/backport-6.12/613-05-v6.19-net-dsa-b53-fix-BCM5325-65-ARL-entry-multicast-port-masks.patch new file mode 100644 index 00000000000..c0f5b5d6003 --- /dev/null +++ b/target/linux/generic/backport-6.12/613-05-v6.19-net-dsa-b53-fix-BCM5325-65-ARL-entry-multicast-port-masks.patch @@ -0,0 +1,120 @@ +From 3b08863469aa6028ac7c3120966f4e2f6051cf6b Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 28 Nov 2025 09:06:23 +0100 +Subject: [PATCH] net: dsa: b53: fix BCM5325/65 ARL entry multicast port masks + +We currently use the mask 0xf for writing and reading b53_entry::port, +but this is only correct for unicast ARL entries. Multicast ARL entries +use a bitmask, and 0xf is not enough space for ports > 3, which includes +the CPU port. + +So extend the mask accordingly to also fit port 4 (bit 4) and MII (bit +5). According to the datasheet the multicast port mask is [60:48], +making it 12 bit wide, but bits 60-55 are reserved anyway, and collide +with the priority field at [60:59], so I am not sure if this is valid. +Therefore leave it at the actual used range, [53:48]. + +The ARL search result register differs a bit, and there the mask is only +[52:48], so only spanning the user ports. The MII port bit is +contained in the Search Result Extension register. So create a separate +search result parse function that properly handles this. + +Fixes: c45655386e53 ("net: dsa: b53: add support for FDB operations on 5325/5365") +Reviewed-by: Florian Fainelli +Signed-off-by: Jonas Gorski +Link: https://patch.msgid.link/20251128080625.27181-6-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 4 +++- + drivers/net/dsa/b53/b53_priv.h | 25 +++++++++++++++++++++---- + drivers/net/dsa/b53/b53_regs.h | 8 +++++++- + 3 files changed, 31 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2099,10 +2099,12 @@ static void b53_arl_search_read_25(struc + struct b53_arl_entry *ent) + { + u64 mac_vid; ++ u8 ext; + ++ b53_read8(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSLT_EXT_25, &ext); + b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_25, + &mac_vid); +- b53_arl_to_entry_25(ent, mac_vid); ++ b53_arl_search_to_entry_25(ent, mac_vid, ext); + } + + static void b53_arl_search_read_89(struct b53_device *dev, u8 idx, +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -348,8 +348,8 @@ static inline void b53_arl_to_entry_25(s + ent->is_age = !!(mac_vid & ARLTBL_AGE_25); + ent->is_static = !!(mac_vid & ARLTBL_STATIC_25); + u64_to_ether_addr(mac_vid, ent->mac); +- ent->port = (mac_vid >> ARLTBL_DATA_PORT_ID_S_25) & +- ARLTBL_DATA_PORT_ID_MASK_25; ++ ent->port = (mac_vid & ARLTBL_DATA_PORT_ID_MASK_25) >> ++ ARLTBL_DATA_PORT_ID_S_25; + if (is_unicast_ether_addr(ent->mac) && ent->port == B53_CPU_PORT) + ent->port = B53_CPU_PORT_25; + ent->vid = (mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25; +@@ -388,8 +388,8 @@ static inline void b53_arl_from_entry_25 + if (is_unicast_ether_addr(ent->mac) && ent->port == B53_CPU_PORT_25) + *mac_vid |= (u64)B53_CPU_PORT << ARLTBL_DATA_PORT_ID_S_25; + else +- *mac_vid |= (u64)(ent->port & ARLTBL_DATA_PORT_ID_MASK_25) << +- ARLTBL_DATA_PORT_ID_S_25; ++ *mac_vid |= ((u64)ent->port << ARLTBL_DATA_PORT_ID_S_25) & ++ ARLTBL_DATA_PORT_ID_MASK_25; + *mac_vid |= (u64)(ent->vid & ARLTBL_VID_MASK_25) << + ARLTBL_VID_S_65; + if (ent->is_valid) +@@ -414,6 +414,23 @@ static inline void b53_arl_from_entry_89 + *fwd_entry |= ARLTBL_AGE_89; + } + ++static inline void b53_arl_search_to_entry_25(struct b53_arl_entry *ent, ++ u64 mac_vid, u8 ext) ++{ ++ memset(ent, 0, sizeof(*ent)); ++ ent->is_valid = !!(mac_vid & ARLTBL_VALID_25); ++ ent->is_age = !!(mac_vid & ARLTBL_AGE_25); ++ ent->is_static = !!(mac_vid & ARLTBL_STATIC_25); ++ u64_to_ether_addr(mac_vid, ent->mac); ++ ent->vid = (mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25; ++ ent->port = (mac_vid & ARL_SRCH_RSLT_PORT_ID_MASK_25) >> ++ ARL_SRCH_RSLT_PORT_ID_S_25; ++ if (is_multicast_ether_addr(ent->mac) && (ext & ARL_SRCH_RSLT_EXT_MC_MII)) ++ ent->port |= BIT(B53_CPU_PORT_25); ++ else if (!is_multicast_ether_addr(ent->mac) && ent->port == B53_CPU_PORT) ++ ent->port = B53_CPU_PORT_25; ++} ++ + static inline void b53_arl_search_to_entry_63xx(struct b53_arl_entry *ent, + u64 mac_vid, u16 fwd_entry) + { +--- a/drivers/net/dsa/b53/b53_regs.h ++++ b/drivers/net/dsa/b53/b53_regs.h +@@ -328,7 +328,7 @@ + #define ARLTBL_VID_MASK_25 0xff + #define ARLTBL_VID_MASK 0xfff + #define ARLTBL_DATA_PORT_ID_S_25 48 +-#define ARLTBL_DATA_PORT_ID_MASK_25 0xf ++#define ARLTBL_DATA_PORT_ID_MASK_25 GENMASK_ULL(53, 48) + #define ARLTBL_VID_S_65 53 + #define ARLTBL_AGE_25 BIT_ULL(61) + #define ARLTBL_STATIC_25 BIT_ULL(62) +@@ -374,6 +374,12 @@ + + /* Single register search result on 5325/5365 */ + #define B53_ARL_SRCH_RSTL_0_MACVID_25 0x24 ++#define ARL_SRCH_RSLT_PORT_ID_S_25 48 ++#define ARL_SRCH_RSLT_PORT_ID_MASK_25 GENMASK_ULL(52, 48) ++ ++/* BCM5325/5365 Search result extend register (8 bit) */ ++#define B53_ARL_SRCH_RSLT_EXT_25 0x2c ++#define ARL_SRCH_RSLT_EXT_MC_MII BIT(2) + + /* ARL Search Data Result (32 bit) */ + #define B53_ARL_SRCH_RSTL_0 0x68 diff --git a/target/linux/generic/backport-6.12/613-06-v6.19-net-dsa-b53-fix-BCM5325-65-ARL-entry-VIDs.patch b/target/linux/generic/backport-6.12/613-06-v6.19-net-dsa-b53-fix-BCM5325-65-ARL-entry-VIDs.patch new file mode 100644 index 00000000000..a2025f980ca --- /dev/null +++ b/target/linux/generic/backport-6.12/613-06-v6.19-net-dsa-b53-fix-BCM5325-65-ARL-entry-VIDs.patch @@ -0,0 +1,140 @@ +From d39514e6a2d14f57830d649e2bf03b49612c2f73 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 28 Nov 2025 09:06:24 +0100 +Subject: [PATCH] net: dsa: b53: fix BCM5325/65 ARL entry VIDs + +BCM5325/65's ARL entry registers do not contain the VID, only the search +result register does. ARL entries have a separate VID entry register for +the index into the VLAN table. + +So make ARL entry accessors use the VID entry registers instead, and +move the VLAN ID field definition to the search register definition. + +Fixes: c45655386e53 ("net: dsa: b53: add support for FDB operations on 5325/5365") +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251128080625.27181-7-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 9 +++++++-- + drivers/net/dsa/b53/b53_priv.h | 12 ++++++------ + drivers/net/dsa/b53/b53_regs.h | 7 +++++-- + 3 files changed, 18 insertions(+), 10 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1833,19 +1833,24 @@ static int b53_arl_rw_op(struct b53_devi + static void b53_arl_read_entry_25(struct b53_device *dev, + struct b53_arl_entry *ent, u8 idx) + { ++ u8 vid_entry; + u64 mac_vid; + ++ b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_VID_ENTRY_25(idx), ++ &vid_entry); + b53_read64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx), + &mac_vid); +- b53_arl_to_entry_25(ent, mac_vid); ++ b53_arl_to_entry_25(ent, mac_vid, vid_entry); + } + + static void b53_arl_write_entry_25(struct b53_device *dev, + const struct b53_arl_entry *ent, u8 idx) + { ++ u8 vid_entry; + u64 mac_vid; + +- b53_arl_from_entry_25(&mac_vid, ent); ++ b53_arl_from_entry_25(&mac_vid, &vid_entry, ent); ++ b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_VID_ENTRY_25(idx), vid_entry); + b53_write64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx), + mac_vid); + } +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -341,7 +341,7 @@ static inline void b53_arl_to_entry(stru + } + + static inline void b53_arl_to_entry_25(struct b53_arl_entry *ent, +- u64 mac_vid) ++ u64 mac_vid, u8 vid_entry) + { + memset(ent, 0, sizeof(*ent)); + ent->is_valid = !!(mac_vid & ARLTBL_VALID_25); +@@ -352,7 +352,7 @@ static inline void b53_arl_to_entry_25(s + ARLTBL_DATA_PORT_ID_S_25; + if (is_unicast_ether_addr(ent->mac) && ent->port == B53_CPU_PORT) + ent->port = B53_CPU_PORT_25; +- ent->vid = (mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25; ++ ent->vid = vid_entry; + } + + static inline void b53_arl_to_entry_89(struct b53_arl_entry *ent, +@@ -381,7 +381,7 @@ static inline void b53_arl_from_entry(u6 + *fwd_entry |= ARLTBL_AGE; + } + +-static inline void b53_arl_from_entry_25(u64 *mac_vid, ++static inline void b53_arl_from_entry_25(u64 *mac_vid, u8 *vid_entry, + const struct b53_arl_entry *ent) + { + *mac_vid = ether_addr_to_u64(ent->mac); +@@ -390,14 +390,13 @@ static inline void b53_arl_from_entry_25 + else + *mac_vid |= ((u64)ent->port << ARLTBL_DATA_PORT_ID_S_25) & + ARLTBL_DATA_PORT_ID_MASK_25; +- *mac_vid |= (u64)(ent->vid & ARLTBL_VID_MASK_25) << +- ARLTBL_VID_S_65; + if (ent->is_valid) + *mac_vid |= ARLTBL_VALID_25; + if (ent->is_static) + *mac_vid |= ARLTBL_STATIC_25; + if (ent->is_age) + *mac_vid |= ARLTBL_AGE_25; ++ *vid_entry = ent->vid; + } + + static inline void b53_arl_from_entry_89(u64 *mac_vid, u32 *fwd_entry, +@@ -422,7 +421,8 @@ static inline void b53_arl_search_to_ent + ent->is_age = !!(mac_vid & ARLTBL_AGE_25); + ent->is_static = !!(mac_vid & ARLTBL_STATIC_25); + u64_to_ether_addr(mac_vid, ent->mac); +- ent->vid = (mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25; ++ ent->vid = (mac_vid & ARL_SRCH_RSLT_VID_MASK_25) >> ++ ARL_SRCH_RSLT_VID_S_25; + ent->port = (mac_vid & ARL_SRCH_RSLT_PORT_ID_MASK_25) >> + ARL_SRCH_RSLT_PORT_ID_S_25; + if (is_multicast_ether_addr(ent->mac) && (ext & ARL_SRCH_RSLT_EXT_MC_MII)) +--- a/drivers/net/dsa/b53/b53_regs.h ++++ b/drivers/net/dsa/b53/b53_regs.h +@@ -325,11 +325,9 @@ + #define B53_ARLTBL_MAC_VID_ENTRY(n) ((0x10 * (n)) + 0x10) + #define ARLTBL_MAC_MASK 0xffffffffffffULL + #define ARLTBL_VID_S 48 +-#define ARLTBL_VID_MASK_25 0xff + #define ARLTBL_VID_MASK 0xfff + #define ARLTBL_DATA_PORT_ID_S_25 48 + #define ARLTBL_DATA_PORT_ID_MASK_25 GENMASK_ULL(53, 48) +-#define ARLTBL_VID_S_65 53 + #define ARLTBL_AGE_25 BIT_ULL(61) + #define ARLTBL_STATIC_25 BIT_ULL(62) + #define ARLTBL_VALID_25 BIT_ULL(63) +@@ -349,6 +347,9 @@ + #define ARLTBL_STATIC_89 BIT(14) + #define ARLTBL_VALID_89 BIT(15) + ++/* BCM5325/BCM565 ARL Table VID Entry N Registers (8 bit) */ ++#define B53_ARLTBL_VID_ENTRY_25(n) ((0x2 * (n)) + 0x30) ++ + /* Maximum number of bin entries in the ARL for all switches */ + #define B53_ARLTBL_MAX_BIN_ENTRIES 4 + +@@ -376,6 +377,8 @@ + #define B53_ARL_SRCH_RSTL_0_MACVID_25 0x24 + #define ARL_SRCH_RSLT_PORT_ID_S_25 48 + #define ARL_SRCH_RSLT_PORT_ID_MASK_25 GENMASK_ULL(52, 48) ++#define ARL_SRCH_RSLT_VID_S_25 53 ++#define ARL_SRCH_RSLT_VID_MASK_25 GENMASK_ULL(60, 53) + + /* BCM5325/5365 Search result extend register (8 bit) */ + #define B53_ARL_SRCH_RSLT_EXT_25 0x2c diff --git a/target/linux/generic/backport-6.12/613-07-v6.19-net-dsa-b53-allow-VID-0-for-BCM5325-65.patch b/target/linux/generic/backport-6.12/613-07-v6.19-net-dsa-b53-allow-VID-0-for-BCM5325-65.patch new file mode 100644 index 00000000000..7c6a63fc918 --- /dev/null +++ b/target/linux/generic/backport-6.12/613-07-v6.19-net-dsa-b53-allow-VID-0-for-BCM5325-65.patch @@ -0,0 +1,44 @@ +From 0b2b27058692d437b12d3f2a3bf0fa699af7376e Mon Sep 17 00:00:00 2001 +From: Jonas Gorski +Date: Fri, 28 Nov 2025 09:06:25 +0100 +Subject: [PATCH] net: dsa: b53: allow VID 0 for BCM5325/65 + +Now that writing ARL entries works properly, we can actually use VID 0 +as the default untagged VLAN for BCM5325 and BCM5365 as well. + +So use 0 as default PVID for all chips and do not reject VLAN 0 anymore, +which we ignored since commit 45e9d59d3950 ("net: dsa: b53: do not allow +to configure VLAN 0") anyway. + +Signed-off-by: Jonas Gorski +Reviewed-by: Florian Fainelli +Link: https://patch.msgid.link/20251128080625.27181-8-jonas.gorski@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_common.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -852,10 +852,7 @@ static void b53_enable_stp(struct b53_de + + static u16 b53_default_pvid(struct b53_device *dev) + { +- if (is5325(dev) || is5365(dev)) +- return 1; +- else +- return 0; ++ return 0; + } + + static bool b53_vlan_port_needs_forced_tagged(struct dsa_switch *ds, int port) +@@ -1679,9 +1676,6 @@ static int b53_vlan_prepare(struct dsa_s + { + struct b53_device *dev = ds->priv; + +- if ((is5325(dev) || is5365(dev)) && vlan->vid == 0) +- return -EOPNOTSUPP; +- + /* Port 7 on 7278 connects to the ASP's UniMAC which is not capable of + * receiving VLAN tagged frames at all, we can still allow the port to + * be configured for egress untagged. diff --git a/target/linux/generic/backport-6.12/620-v6.15-ppp-use-IFF_NO_QUEUE-in-virtual-interfaces.patch b/target/linux/generic/backport-6.12/620-v6.15-ppp-use-IFF_NO_QUEUE-in-virtual-interfaces.patch new file mode 100644 index 00000000000..333627af1b9 --- /dev/null +++ b/target/linux/generic/backport-6.12/620-v6.15-ppp-use-IFF_NO_QUEUE-in-virtual-interfaces.patch @@ -0,0 +1,79 @@ +From: Qingfang Deng +Date: Sat, 1 Mar 2025 21:55:16 +0800 +Subject: [PATCH] ppp: use IFF_NO_QUEUE in virtual interfaces +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For PPPoE, PPTP, and PPPoL2TP, the start_xmit() function directly +forwards packets to the underlying network stack and never returns +anything other than 1. So these interfaces do not require a qdisc, +and the IFF_NO_QUEUE flag should be set. + +Introduces a direct_xmit flag in struct ppp_channel to indicate when +IFF_NO_QUEUE should be applied. The flag is set in ppp_connect_channel() +for relevant protocols. + +While at it, remove the usused latency member from struct ppp_channel. + +Signed-off-by: Qingfang Deng +Reviewed-by: Toke Høiland-Jørgensen +Link: https://patch.msgid.link/20250301135517.695809-1-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -3504,6 +3504,10 @@ ppp_connect_channel(struct channel *pch, + ret = -ENOTCONN; + goto outl; + } ++ if (pch->chan->direct_xmit) ++ ppp->dev->priv_flags |= IFF_NO_QUEUE; ++ else ++ ppp->dev->priv_flags &= ~IFF_NO_QUEUE; + spin_unlock_bh(&pch->downl); + if (pch->file.hdrlen > ppp->file.hdrlen) + ppp->file.hdrlen = pch->file.hdrlen; +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -693,6 +693,7 @@ static int pppoe_connect(struct socket * + po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr) - 2; + po->chan.private = sk; + po->chan.ops = &pppoe_chan_ops; ++ po->chan.direct_xmit = true; + + error = ppp_register_net_channel(dev_net(dev), &po->chan); + if (error) { +--- a/drivers/net/ppp/pptp.c ++++ b/drivers/net/ppp/pptp.c +@@ -469,6 +469,7 @@ static int pptp_connect(struct socket *s + po->chan.mtu -= PPTP_HEADER_OVERHEAD; + + po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header); ++ po->chan.direct_xmit = true; + error = ppp_register_channel(&po->chan); + if (error) { + pr_err("PPTP: failed to register PPP channel (%d)\n", error); +--- a/include/linux/ppp_channel.h ++++ b/include/linux/ppp_channel.h +@@ -42,8 +42,7 @@ struct ppp_channel { + int hdrlen; /* amount of headroom channel needs */ + void *ppp; /* opaque to channel */ + int speed; /* transfer rate (bytes/second) */ +- /* the following is not used at present */ +- int latency; /* overhead time in milliseconds */ ++ bool direct_xmit; /* no qdisc, xmit directly */ + }; + + #ifdef __KERNEL__ +--- a/net/l2tp/l2tp_ppp.c ++++ b/net/l2tp/l2tp_ppp.c +@@ -796,6 +796,7 @@ static int pppol2tp_connect(struct socke + po->chan.private = sk; + po->chan.ops = &pppol2tp_chan_ops; + po->chan.mtu = pppol2tp_tunnel_mtu(tunnel); ++ po->chan.direct_xmit = true; + + error = ppp_register_net_channel(sock_net(sk), &po->chan); + if (error) { diff --git a/target/linux/generic/backport-6.12/621-v6.17-ppp-convert-to-percpu-netstats.patch b/target/linux/generic/backport-6.12/621-v6.17-ppp-convert-to-percpu-netstats.patch new file mode 100644 index 00000000000..874d2bfd8ac --- /dev/null +++ b/target/linux/generic/backport-6.12/621-v6.17-ppp-convert-to-percpu-netstats.patch @@ -0,0 +1,126 @@ +From 1a3e9b7a6b09e8ab3d2af019e4a392622685855e Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Tue, 10 Jun 2025 16:32:10 +0800 +Subject: [PATCH] ppp: convert to percpu netstats + +Convert to percpu netstats to avoid lock contention when reading them. + +Signed-off-by: Qingfang Deng +Link: https://patch.msgid.link/20250610083211.909015-1-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ppp/ppp_generic.c | 52 +++++++++++++---------------------- + 1 file changed, 19 insertions(+), 33 deletions(-) + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -108,18 +108,6 @@ struct ppp_file { + #define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel) + + /* +- * Data structure to hold primary network stats for which +- * we want to use 64 bit storage. Other network stats +- * are stored in dev->stats of the ppp strucute. +- */ +-struct ppp_link_stats { +- u64 rx_packets; +- u64 tx_packets; +- u64 rx_bytes; +- u64 tx_bytes; +-}; +- +-/* + * Data structure describing one ppp unit. + * A ppp unit corresponds to a ppp network interface device + * and represents a multilink bundle. +@@ -162,7 +150,6 @@ struct ppp { + struct bpf_prog *active_filter; /* filter for pkts to reset idle */ + #endif /* CONFIG_PPP_FILTER */ + struct net *ppp_net; /* the net we belong to */ +- struct ppp_link_stats stats64; /* 64 bit network stats */ + }; + + /* +@@ -1541,23 +1528,12 @@ ppp_net_siocdevprivate(struct net_device + static void + ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64) + { +- struct ppp *ppp = netdev_priv(dev); +- +- ppp_recv_lock(ppp); +- stats64->rx_packets = ppp->stats64.rx_packets; +- stats64->rx_bytes = ppp->stats64.rx_bytes; +- ppp_recv_unlock(ppp); +- +- ppp_xmit_lock(ppp); +- stats64->tx_packets = ppp->stats64.tx_packets; +- stats64->tx_bytes = ppp->stats64.tx_bytes; +- ppp_xmit_unlock(ppp); +- + stats64->rx_errors = dev->stats.rx_errors; + stats64->tx_errors = dev->stats.tx_errors; + stats64->rx_dropped = dev->stats.rx_dropped; + stats64->tx_dropped = dev->stats.tx_dropped; + stats64->rx_length_errors = dev->stats.rx_length_errors; ++ dev_fetch_sw_netstats(stats64, dev->tstats); + } + + static int ppp_dev_init(struct net_device *dev) +@@ -1655,6 +1631,7 @@ static void ppp_setup(struct net_device + dev->type = ARPHRD_PPP; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->priv_destructor = ppp_dev_priv_destructor; ++ dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + netif_keep_dst(dev); + } + +@@ -1800,8 +1777,7 @@ ppp_send_frame(struct ppp *ppp, struct s + #endif /* CONFIG_PPP_FILTER */ + } + +- ++ppp->stats64.tx_packets; +- ppp->stats64.tx_bytes += skb->len - PPP_PROTO_LEN; ++ dev_sw_netstats_tx_add(ppp->dev, 1, skb->len - PPP_PROTO_LEN); + + switch (proto) { + case PPP_IP: +@@ -2479,8 +2455,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, + break; + } + +- ++ppp->stats64.rx_packets; +- ppp->stats64.rx_bytes += skb->len - 2; ++ dev_sw_netstats_rx_add(ppp->dev, skb->len - PPP_PROTO_LEN); + + npi = proto_to_npindex(proto); + if (npi < 0) { +@@ -3308,14 +3283,25 @@ static void + ppp_get_stats(struct ppp *ppp, struct ppp_stats *st) + { + struct slcompress *vj = ppp->vj; ++ int cpu; + + memset(st, 0, sizeof(*st)); +- st->p.ppp_ipackets = ppp->stats64.rx_packets; ++ for_each_possible_cpu(cpu) { ++ struct pcpu_sw_netstats *p = per_cpu_ptr(ppp->dev->tstats, cpu); ++ u64 rx_packets, rx_bytes, tx_packets, tx_bytes; ++ ++ rx_packets = u64_stats_read(&p->rx_packets); ++ rx_bytes = u64_stats_read(&p->rx_bytes); ++ tx_packets = u64_stats_read(&p->tx_packets); ++ tx_bytes = u64_stats_read(&p->tx_bytes); ++ ++ st->p.ppp_ipackets += rx_packets; ++ st->p.ppp_ibytes += rx_bytes; ++ st->p.ppp_opackets += tx_packets; ++ st->p.ppp_obytes += tx_bytes; ++ } + st->p.ppp_ierrors = ppp->dev->stats.rx_errors; +- st->p.ppp_ibytes = ppp->stats64.rx_bytes; +- st->p.ppp_opackets = ppp->stats64.tx_packets; + st->p.ppp_oerrors = ppp->dev->stats.tx_errors; +- st->p.ppp_obytes = ppp->stats64.tx_bytes; + if (!vj) + return; + st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed; diff --git a/target/linux/generic/backport-6.12/622-v6.18-ppp-remove-rwlock-usage.patch b/target/linux/generic/backport-6.12/622-v6.18-ppp-remove-rwlock-usage.patch new file mode 100644 index 00000000000..f66a5222244 --- /dev/null +++ b/target/linux/generic/backport-6.12/622-v6.18-ppp-remove-rwlock-usage.patch @@ -0,0 +1,334 @@ +From b8844aab519a154808dbce15a132f3e8f1c34af6 Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Fri, 22 Aug 2025 09:25:47 +0800 +Subject: [PATCH] ppp: remove rwlock usage + +In struct channel, the upl lock is implemented using rwlock_t, +protecting access to pch->ppp and pch->bridge. + +As previously discussed on the list, using rwlock in the network fast +path is not recommended. +This patch replaces the rwlock with a spinlock for writers, and uses RCU +for readers. + +- pch->ppp and pch->bridge are now declared as __rcu pointers. +- Readers use rcu_dereference_bh() under rcu_read_lock_bh(). +- Writers use spin_lock() to update. + +Signed-off-by: Qingfang Deng +Link: https://patch.msgid.link/20250822012548.6232-1-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ppp/ppp_generic.c | 120 ++++++++++++++++++---------------- + 1 file changed, 63 insertions(+), 57 deletions(-) + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -173,11 +173,11 @@ struct channel { + struct ppp_channel *chan; /* public channel data structure */ + struct rw_semaphore chan_sem; /* protects `chan' during chan ioctl */ + spinlock_t downl; /* protects `chan', file.xq dequeue */ +- struct ppp *ppp; /* ppp unit we're connected to */ ++ struct ppp __rcu *ppp; /* ppp unit we're connected to */ + struct net *chan_net; /* the net channel belongs to */ + netns_tracker ns_tracker; + struct list_head clist; /* link in list of channels per unit */ +- rwlock_t upl; /* protects `ppp' and 'bridge' */ ++ spinlock_t upl; /* protects `ppp' and 'bridge' */ + struct channel __rcu *bridge; /* "bridged" ppp channel */ + #ifdef CONFIG_PPP_MULTILINK + u8 avail; /* flag used in multilink stuff */ +@@ -639,34 +639,34 @@ static struct bpf_prog *compat_ppp_get_f + */ + static int ppp_bridge_channels(struct channel *pch, struct channel *pchb) + { +- write_lock_bh(&pch->upl); +- if (pch->ppp || ++ spin_lock(&pch->upl); ++ if (rcu_dereference_protected(pch->ppp, lockdep_is_held(&pch->upl)) || + rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl))) { +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + return -EALREADY; + } + refcount_inc(&pchb->file.refcnt); + rcu_assign_pointer(pch->bridge, pchb); +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + +- write_lock_bh(&pchb->upl); +- if (pchb->ppp || ++ spin_lock(&pchb->upl); ++ if (rcu_dereference_protected(pchb->ppp, lockdep_is_held(&pchb->upl)) || + rcu_dereference_protected(pchb->bridge, lockdep_is_held(&pchb->upl))) { +- write_unlock_bh(&pchb->upl); ++ spin_unlock(&pchb->upl); + goto err_unset; + } + refcount_inc(&pch->file.refcnt); + rcu_assign_pointer(pchb->bridge, pch); +- write_unlock_bh(&pchb->upl); ++ spin_unlock(&pchb->upl); + + return 0; + + err_unset: +- write_lock_bh(&pch->upl); ++ spin_lock(&pch->upl); + /* Re-read pch->bridge with upl held in case it was modified concurrently */ + pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl)); + RCU_INIT_POINTER(pch->bridge, NULL); +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + synchronize_rcu(); + + if (pchb) +@@ -680,25 +680,25 @@ static int ppp_unbridge_channels(struct + { + struct channel *pchb, *pchbb; + +- write_lock_bh(&pch->upl); ++ spin_lock(&pch->upl); + pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl)); + if (!pchb) { +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + return -EINVAL; + } + RCU_INIT_POINTER(pch->bridge, NULL); +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + + /* Only modify pchb if phcb->bridge points back to pch. + * If not, it implies that there has been a race unbridging (and possibly + * even rebridging) pchb. We should leave pchb alone to avoid either a + * refcount underflow, or breaking another established bridge instance. + */ +- write_lock_bh(&pchb->upl); ++ spin_lock(&pchb->upl); + pchbb = rcu_dereference_protected(pchb->bridge, lockdep_is_held(&pchb->upl)); + if (pchbb == pch) + RCU_INIT_POINTER(pchb->bridge, NULL); +- write_unlock_bh(&pchb->upl); ++ spin_unlock(&pchb->upl); + + synchronize_rcu(); + +@@ -2144,10 +2144,9 @@ static int ppp_mp_explode(struct ppp *pp + #endif /* CONFIG_PPP_MULTILINK */ + + /* Try to send data out on a channel */ +-static void __ppp_channel_push(struct channel *pch) ++static void __ppp_channel_push(struct channel *pch, struct ppp *ppp) + { + struct sk_buff *skb; +- struct ppp *ppp; + + spin_lock(&pch->downl); + if (pch->chan) { +@@ -2166,7 +2165,6 @@ static void __ppp_channel_push(struct ch + spin_unlock(&pch->downl); + /* see if there is anything from the attached unit to be sent */ + if (skb_queue_empty(&pch->file.xq)) { +- ppp = pch->ppp; + if (ppp) + __ppp_xmit_process(ppp, NULL); + } +@@ -2174,15 +2172,18 @@ static void __ppp_channel_push(struct ch + + static void ppp_channel_push(struct channel *pch) + { +- read_lock_bh(&pch->upl); +- if (pch->ppp) { +- (*this_cpu_ptr(pch->ppp->xmit_recursion))++; +- __ppp_channel_push(pch); +- (*this_cpu_ptr(pch->ppp->xmit_recursion))--; ++ struct ppp *ppp; ++ ++ rcu_read_lock_bh(); ++ ppp = rcu_dereference_bh(pch->ppp); ++ if (ppp) { ++ (*this_cpu_ptr(ppp->xmit_recursion))++; ++ __ppp_channel_push(pch, ppp); ++ (*this_cpu_ptr(ppp->xmit_recursion))--; + } else { +- __ppp_channel_push(pch); ++ __ppp_channel_push(pch, NULL); + } +- read_unlock_bh(&pch->upl); ++ rcu_read_unlock_bh(); + } + + /* +@@ -2284,6 +2285,7 @@ void + ppp_input(struct ppp_channel *chan, struct sk_buff *skb) + { + struct channel *pch = chan->ppp; ++ struct ppp *ppp; + int proto; + + if (!pch) { +@@ -2295,18 +2297,19 @@ ppp_input(struct ppp_channel *chan, stru + if (ppp_channel_bridge_input(pch, skb)) + return; + +- read_lock_bh(&pch->upl); ++ rcu_read_lock_bh(); ++ ppp = rcu_dereference_bh(pch->ppp); + if (!ppp_decompress_proto(skb)) { + kfree_skb(skb); +- if (pch->ppp) { +- ++pch->ppp->dev->stats.rx_length_errors; +- ppp_receive_error(pch->ppp); ++ if (ppp) { ++ ++ppp->dev->stats.rx_length_errors; ++ ppp_receive_error(ppp); + } + goto done; + } + + proto = PPP_PROTO(skb); +- if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) { ++ if (!ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) { + /* put it on the channel queue */ + skb_queue_tail(&pch->file.rq, skb); + /* drop old frames if queue too long */ +@@ -2315,11 +2318,11 @@ ppp_input(struct ppp_channel *chan, stru + kfree_skb(skb); + wake_up_interruptible(&pch->file.rwait); + } else { +- ppp_do_recv(pch->ppp, skb, pch); ++ ppp_do_recv(ppp, skb, pch); + } + + done: +- read_unlock_bh(&pch->upl); ++ rcu_read_unlock_bh(); + } + + /* Put a 0-length skb in the receive queue as an error indication */ +@@ -2328,20 +2331,22 @@ ppp_input_error(struct ppp_channel *chan + { + struct channel *pch = chan->ppp; + struct sk_buff *skb; ++ struct ppp *ppp; + + if (!pch) + return; + +- read_lock_bh(&pch->upl); +- if (pch->ppp) { ++ rcu_read_lock_bh(); ++ ppp = rcu_dereference_bh(pch->ppp); ++ if (ppp) { + skb = alloc_skb(0, GFP_ATOMIC); + if (skb) { + skb->len = 0; /* probably unnecessary */ + skb->cb[0] = code; +- ppp_do_recv(pch->ppp, skb, pch); ++ ppp_do_recv(ppp, skb, pch); + } + } +- read_unlock_bh(&pch->upl); ++ rcu_read_unlock_bh(); + } + + /* +@@ -2889,7 +2894,6 @@ int ppp_register_net_channel(struct net + + pn = ppp_pernet(net); + +- pch->ppp = NULL; + pch->chan = chan; + pch->chan_net = get_net_track(net, &pch->ns_tracker, GFP_KERNEL); + chan->ppp = pch; +@@ -2900,7 +2904,7 @@ int ppp_register_net_channel(struct net + #endif /* CONFIG_PPP_MULTILINK */ + init_rwsem(&pch->chan_sem); + spin_lock_init(&pch->downl); +- rwlock_init(&pch->upl); ++ spin_lock_init(&pch->upl); + + spin_lock_bh(&pn->all_channels_lock); + pch->file.index = ++pn->last_channel_index; +@@ -2929,13 +2933,15 @@ int ppp_channel_index(struct ppp_channel + int ppp_unit_number(struct ppp_channel *chan) + { + struct channel *pch = chan->ppp; ++ struct ppp *ppp; + int unit = -1; + + if (pch) { +- read_lock_bh(&pch->upl); +- if (pch->ppp) +- unit = pch->ppp->file.index; +- read_unlock_bh(&pch->upl); ++ rcu_read_lock(); ++ ppp = rcu_dereference(pch->ppp); ++ if (ppp) ++ unit = ppp->file.index; ++ rcu_read_unlock(); + } + return unit; + } +@@ -2947,12 +2953,14 @@ char *ppp_dev_name(struct ppp_channel *c + { + struct channel *pch = chan->ppp; + char *name = NULL; ++ struct ppp *ppp; + + if (pch) { +- read_lock_bh(&pch->upl); +- if (pch->ppp && pch->ppp->dev) +- name = pch->ppp->dev->name; +- read_unlock_bh(&pch->upl); ++ rcu_read_lock(); ++ ppp = rcu_dereference(pch->ppp); ++ if (ppp && ppp->dev) ++ name = ppp->dev->name; ++ rcu_read_unlock(); + } + return name; + } +@@ -3475,9 +3483,9 @@ ppp_connect_channel(struct channel *pch, + ppp = ppp_find_unit(pn, unit); + if (!ppp) + goto out; +- write_lock_bh(&pch->upl); ++ spin_lock(&pch->upl); + ret = -EINVAL; +- if (pch->ppp || ++ if (rcu_dereference_protected(pch->ppp, lockdep_is_held(&pch->upl)) || + rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl))) + goto outl; + +@@ -3502,13 +3510,13 @@ ppp_connect_channel(struct channel *pch, + ppp->dev->hard_header_len = hdrlen; + list_add_tail_rcu(&pch->clist, &ppp->channels); + ++ppp->n_channels; +- pch->ppp = ppp; ++ rcu_assign_pointer(pch->ppp, ppp); + refcount_inc(&ppp->file.refcnt); + ppp_unlock(ppp); + ret = 0; + + outl: +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + out: + mutex_unlock(&pn->all_ppp_mutex); + return ret; +@@ -3523,10 +3531,9 @@ ppp_disconnect_channel(struct channel *p + struct ppp *ppp; + int err = -EINVAL; + +- write_lock_bh(&pch->upl); +- ppp = pch->ppp; +- pch->ppp = NULL; +- write_unlock_bh(&pch->upl); ++ spin_lock(&pch->upl); ++ ppp = rcu_replace_pointer(pch->ppp, NULL, lockdep_is_held(&pch->upl)); ++ spin_unlock(&pch->upl); + if (ppp) { + /* remove it from the ppp unit's list */ + ppp_lock(ppp); diff --git a/target/linux/generic/backport-6.12/623-v6.18-pppoe-remove-rwlock-usage.patch b/target/linux/generic/backport-6.12/623-v6.18-pppoe-remove-rwlock-usage.patch new file mode 100644 index 00000000000..619f1cbf77b --- /dev/null +++ b/target/linux/generic/backport-6.12/623-v6.18-pppoe-remove-rwlock-usage.patch @@ -0,0 +1,321 @@ +From 72cdc67e7fa74931b055df3a76852bab551f1a04 Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Thu, 28 Aug 2025 09:20:16 +0800 +Subject: [PATCH] pppoe: remove rwlock usage + +Like ppp_generic.c, convert the PPPoE socket hash table to use RCU for +lookups and a spinlock for updates. This removes rwlock usage and allows +lockless readers on the fast path. + +- Mark hash table and list pointers as __rcu. +- Use spin_lock() to protect writers. +- Readers use rcu_dereference() under rcu_read_lock(). All known callers + of get_item() already hold the RCU read lock, so no additional locking + is needed. +- get_item() now uses refcount_inc_not_zero() instead of sock_hold() to + safely take a reference. This prevents crashes if a socket is already + in the process of being freed (sk_refcnt == 0). +- Set SOCK_RCU_FREE to defer socket freeing until after an RCU grace + period. +- Move skb_queue_purge() into sk_destruct callback to ensure purge + happens after an RCU grace period. + +Signed-off-by: Qingfang Deng +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20250828012018.15922-1-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ppp/pppoe.c | 94 ++++++++++++++++++++++------------------ + include/linux/if_pppox.h | 2 +- + 2 files changed, 54 insertions(+), 42 deletions(-) + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -100,8 +100,8 @@ struct pppoe_net { + * as well, moreover in case of SMP less locking + * controversy here + */ +- struct pppox_sock *hash_table[PPPOE_HASH_SIZE]; +- rwlock_t hash_lock; ++ struct pppox_sock __rcu *hash_table[PPPOE_HASH_SIZE]; ++ spinlock_t hash_lock; + }; + + /* +@@ -162,13 +162,13 @@ static struct pppox_sock *__get_item(str + int hash = hash_item(sid, addr); + struct pppox_sock *ret; + +- ret = pn->hash_table[hash]; ++ ret = rcu_dereference(pn->hash_table[hash]); + while (ret) { + if (cmp_addr(&ret->pppoe_pa, sid, addr) && + ret->pppoe_ifindex == ifindex) + return ret; + +- ret = ret->next; ++ ret = rcu_dereference(ret->next); + } + + return NULL; +@@ -177,19 +177,20 @@ static struct pppox_sock *__get_item(str + static int __set_item(struct pppoe_net *pn, struct pppox_sock *po) + { + int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote); +- struct pppox_sock *ret; ++ struct pppox_sock *ret, *first; + +- ret = pn->hash_table[hash]; ++ first = rcu_dereference_protected(pn->hash_table[hash], lockdep_is_held(&pn->hash_lock)); ++ ret = first; + while (ret) { + if (cmp_2_addr(&ret->pppoe_pa, &po->pppoe_pa) && + ret->pppoe_ifindex == po->pppoe_ifindex) + return -EALREADY; + +- ret = ret->next; ++ ret = rcu_dereference_protected(ret->next, lockdep_is_held(&pn->hash_lock)); + } + +- po->next = pn->hash_table[hash]; +- pn->hash_table[hash] = po; ++ RCU_INIT_POINTER(po->next, first); ++ rcu_assign_pointer(pn->hash_table[hash], po); + + return 0; + } +@@ -198,20 +199,24 @@ static void __delete_item(struct pppoe_n + char *addr, int ifindex) + { + int hash = hash_item(sid, addr); +- struct pppox_sock *ret, **src; ++ struct pppox_sock *ret, __rcu **src; + +- ret = pn->hash_table[hash]; ++ ret = rcu_dereference_protected(pn->hash_table[hash], lockdep_is_held(&pn->hash_lock)); + src = &pn->hash_table[hash]; + + while (ret) { + if (cmp_addr(&ret->pppoe_pa, sid, addr) && + ret->pppoe_ifindex == ifindex) { +- *src = ret->next; ++ struct pppox_sock *next; ++ ++ next = rcu_dereference_protected(ret->next, ++ lockdep_is_held(&pn->hash_lock)); ++ rcu_assign_pointer(*src, next); + break; + } + + src = &ret->next; +- ret = ret->next; ++ ret = rcu_dereference_protected(ret->next, lockdep_is_held(&pn->hash_lock)); + } + } + +@@ -225,11 +230,9 @@ static inline struct pppox_sock *get_ite + { + struct pppox_sock *po; + +- read_lock_bh(&pn->hash_lock); + po = __get_item(pn, sid, addr, ifindex); +- if (po) +- sock_hold(sk_pppox(po)); +- read_unlock_bh(&pn->hash_lock); ++ if (po && !refcount_inc_not_zero(&sk_pppox(po)->sk_refcnt)) ++ po = NULL; + + return po; + } +@@ -258,9 +261,9 @@ static inline struct pppox_sock *get_ite + static inline void delete_item(struct pppoe_net *pn, __be16 sid, + char *addr, int ifindex) + { +- write_lock_bh(&pn->hash_lock); ++ spin_lock(&pn->hash_lock); + __delete_item(pn, sid, addr, ifindex); +- write_unlock_bh(&pn->hash_lock); ++ spin_unlock(&pn->hash_lock); + } + + /*************************************************************************** +@@ -276,14 +279,16 @@ static void pppoe_flush_dev(struct net_d + int i; + + pn = pppoe_pernet(dev_net(dev)); +- write_lock_bh(&pn->hash_lock); ++ spin_lock(&pn->hash_lock); + for (i = 0; i < PPPOE_HASH_SIZE; i++) { +- struct pppox_sock *po = pn->hash_table[i]; ++ struct pppox_sock *po = rcu_dereference_protected(pn->hash_table[i], ++ lockdep_is_held(&pn->hash_lock)); + struct sock *sk; + + while (po) { + while (po && po->pppoe_dev != dev) { +- po = po->next; ++ po = rcu_dereference_protected(po->next, ++ lockdep_is_held(&pn->hash_lock)); + } + + if (!po) +@@ -300,7 +305,7 @@ static void pppoe_flush_dev(struct net_d + */ + + sock_hold(sk); +- write_unlock_bh(&pn->hash_lock); ++ spin_unlock(&pn->hash_lock); + lock_sock(sk); + + if (po->pppoe_dev == dev && +@@ -320,11 +325,12 @@ static void pppoe_flush_dev(struct net_d + */ + + BUG_ON(pppoe_pernet(dev_net(dev)) == NULL); +- write_lock_bh(&pn->hash_lock); +- po = pn->hash_table[i]; ++ spin_lock(&pn->hash_lock); ++ po = rcu_dereference_protected(pn->hash_table[i], ++ lockdep_is_held(&pn->hash_lock)); + } + } +- write_unlock_bh(&pn->hash_lock); ++ spin_unlock(&pn->hash_lock); + } + + static int pppoe_device_event(struct notifier_block *this, +@@ -528,6 +534,11 @@ static struct proto pppoe_sk_proto __rea + .obj_size = sizeof(struct pppox_sock), + }; + ++static void pppoe_destruct(struct sock *sk) ++{ ++ skb_queue_purge(&sk->sk_receive_queue); ++} ++ + /*********************************************************************** + * + * Initialize a new struct sock. +@@ -542,11 +553,13 @@ static int pppoe_create(struct net *net, + return -ENOMEM; + + sock_init_data(sock, sk); ++ sock_set_flag(sk, SOCK_RCU_FREE); + + sock->state = SS_UNCONNECTED; + sock->ops = &pppoe_ops; + + sk->sk_backlog_rcv = pppoe_rcv_core; ++ sk->sk_destruct = pppoe_destruct; + sk->sk_state = PPPOX_NONE; + sk->sk_type = SOCK_STREAM; + sk->sk_family = PF_PPPOX; +@@ -599,7 +612,6 @@ static int pppoe_release(struct socket * + sock_orphan(sk); + sock->sk = NULL; + +- skb_queue_purge(&sk->sk_receive_queue); + release_sock(sk); + sock_put(sk); + +@@ -681,9 +693,9 @@ static int pppoe_connect(struct socket * + &sp->sa_addr.pppoe, + sizeof(struct pppoe_addr)); + +- write_lock_bh(&pn->hash_lock); ++ spin_lock(&pn->hash_lock); + error = __set_item(pn, po); +- write_unlock_bh(&pn->hash_lock); ++ spin_unlock(&pn->hash_lock); + if (error < 0) + goto err_put; + +@@ -1052,11 +1064,11 @@ static inline struct pppox_sock *pppoe_g + int i; + + for (i = 0; i < PPPOE_HASH_SIZE; i++) { +- po = pn->hash_table[i]; ++ po = rcu_dereference(pn->hash_table[i]); + while (po) { + if (!pos--) + goto out; +- po = po->next; ++ po = rcu_dereference(po->next); + } + } + +@@ -1065,19 +1077,19 @@ out: + } + + static void *pppoe_seq_start(struct seq_file *seq, loff_t *pos) +- __acquires(pn->hash_lock) ++ __acquires(RCU) + { + struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq)); + loff_t l = *pos; + +- read_lock_bh(&pn->hash_lock); ++ rcu_read_lock(); + return l ? pppoe_get_idx(pn, --l) : SEQ_START_TOKEN; + } + + static void *pppoe_seq_next(struct seq_file *seq, void *v, loff_t *pos) + { + struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq)); +- struct pppox_sock *po; ++ struct pppox_sock *po, *next; + + ++*pos; + if (v == SEQ_START_TOKEN) { +@@ -1085,14 +1097,15 @@ static void *pppoe_seq_next(struct seq_f + goto out; + } + po = v; +- if (po->next) +- po = po->next; ++ next = rcu_dereference(po->next); ++ if (next) ++ po = next; + else { + int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote); + + po = NULL; + while (++hash < PPPOE_HASH_SIZE) { +- po = pn->hash_table[hash]; ++ po = rcu_dereference(pn->hash_table[hash]); + if (po) + break; + } +@@ -1103,10 +1116,9 @@ out: + } + + static void pppoe_seq_stop(struct seq_file *seq, void *v) +- __releases(pn->hash_lock) ++ __releases(RCU) + { +- struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq)); +- read_unlock_bh(&pn->hash_lock); ++ rcu_read_unlock(); + } + + static const struct seq_operations pppoe_seq_ops = { +@@ -1149,7 +1161,7 @@ static __net_init int pppoe_init_net(str + struct pppoe_net *pn = pppoe_pernet(net); + struct proc_dir_entry *pde; + +- rwlock_init(&pn->hash_lock); ++ spin_lock_init(&pn->hash_lock); + + pde = proc_create_net("pppoe", 0444, net->proc_net, + &pppoe_seq_ops, sizeof(struct seq_net_private)); +--- a/include/linux/if_pppox.h ++++ b/include/linux/if_pppox.h +@@ -43,7 +43,7 @@ struct pppox_sock { + /* struct sock must be the first member of pppox_sock */ + struct sock sk; + struct ppp_channel chan; +- struct pppox_sock *next; /* for hash table */ ++ struct pppox_sock __rcu *next; /* for hash table */ + union { + struct pppoe_opt pppoe; + struct pptp_opt pptp; diff --git a/target/linux/generic/backport-6.12/624-v6.18-pppoe-drop-sock-reference-counting-on-fast-path.patch b/target/linux/generic/backport-6.12/624-v6.18-pppoe-drop-sock-reference-counting-on-fast-path.patch new file mode 100644 index 00000000000..26376210428 --- /dev/null +++ b/target/linux/generic/backport-6.12/624-v6.18-pppoe-drop-sock-reference-counting-on-fast-path.patch @@ -0,0 +1,124 @@ +From 4f54dff818d7b5b1d84becd5d90bc46e6233c0d7 Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Thu, 28 Aug 2025 09:20:17 +0800 +Subject: [PATCH] pppoe: drop sock reference counting on fast path + +Now that PPPoE sockets are freed via RCU (SOCK_RCU_FREE), it is no longer +necessary to take a reference count when looking up sockets on the receive +path. Readers are protected by RCU, so the socket memory remains valid +until after a grace period. + +Convert fast-path lookups to avoid refcounting: + - Replace get_item() and sk_receive_skb() in pppoe_rcv() with + __get_item() and __sk_receive_skb(). + - Rework get_item_by_addr() into __get_item_by_addr() (no refcount and + move RCU lock into pppoe_ioctl) + - Remove unnecessary sock_put() calls. + +This avoids cacheline bouncing from atomic reference counting and improves +performance on the receive fast path. + +Signed-off-by: Qingfang Deng +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20250828012018.15922-2-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ppp/pppoe.c | 35 +++++++++++++---------------------- + 1 file changed, 13 insertions(+), 22 deletions(-) + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -237,8 +237,8 @@ static inline struct pppox_sock *get_ite + return po; + } + +-static inline struct pppox_sock *get_item_by_addr(struct net *net, +- struct sockaddr_pppox *sp) ++static inline struct pppox_sock *__get_item_by_addr(struct net *net, ++ struct sockaddr_pppox *sp) + { + struct net_device *dev; + struct pppoe_net *pn; +@@ -246,15 +246,13 @@ static inline struct pppox_sock *get_ite + + int ifindex; + +- rcu_read_lock(); + dev = dev_get_by_name_rcu(net, sp->sa_addr.pppoe.dev); + if (dev) { + ifindex = dev->ifindex; + pn = pppoe_pernet(net); +- pppox_sock = get_item(pn, sp->sa_addr.pppoe.sid, +- sp->sa_addr.pppoe.remote, ifindex); ++ pppox_sock = __get_item(pn, sp->sa_addr.pppoe.sid, ++ sp->sa_addr.pppoe.remote, ifindex); + } +- rcu_read_unlock(); + return pppox_sock; + } + +@@ -384,18 +382,16 @@ static int pppoe_rcv_core(struct sock *s + if (sk->sk_state & PPPOX_BOUND) { + ppp_input(&po->chan, skb); + } else if (sk->sk_state & PPPOX_RELAY) { +- relay_po = get_item_by_addr(sock_net(sk), +- &po->pppoe_relay); ++ relay_po = __get_item_by_addr(sock_net(sk), ++ &po->pppoe_relay); + if (relay_po == NULL) + goto abort_kfree; + + if ((sk_pppox(relay_po)->sk_state & PPPOX_CONNECTED) == 0) +- goto abort_put; ++ goto abort_kfree; + + if (!__pppoe_xmit(sk_pppox(relay_po), skb)) +- goto abort_put; +- +- sock_put(sk_pppox(relay_po)); ++ goto abort_kfree; + } else { + if (sock_queue_rcv_skb(sk, skb)) + goto abort_kfree; +@@ -403,9 +399,6 @@ static int pppoe_rcv_core(struct sock *s + + return NET_RX_SUCCESS; + +-abort_put: +- sock_put(sk_pppox(relay_po)); +- + abort_kfree: + kfree_skb(skb); + return NET_RX_DROP; +@@ -447,14 +440,11 @@ static int pppoe_rcv(struct sk_buff *skb + ph = pppoe_hdr(skb); + pn = pppoe_pernet(dev_net(dev)); + +- /* Note that get_item does a sock_hold(), so sk_pppox(po) +- * is known to be safe. +- */ +- po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex); ++ po = __get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex); + if (!po) + goto drop; + +- return sk_receive_skb(sk_pppox(po), skb, 0); ++ return __sk_receive_skb(sk_pppox(po), skb, 0, 1, false); + + drop: + kfree_skb(skb); +@@ -820,11 +810,12 @@ static int pppoe_ioctl(struct socket *so + + /* Check that the socket referenced by the address + actually exists. */ +- relay_po = get_item_by_addr(sock_net(sk), &po->pppoe_relay); ++ rcu_read_lock(); ++ relay_po = __get_item_by_addr(sock_net(sk), &po->pppoe_relay); ++ rcu_read_unlock(); + if (!relay_po) + break; + +- sock_put(sk_pppox(relay_po)); + sk->sk_state |= PPPOX_RELAY; + err = 0; + break; diff --git a/target/linux/generic/backport-6.12/625-v7.0-ppp-enable-TX-scatter-gather.patch b/target/linux/generic/backport-6.12/625-v7.0-ppp-enable-TX-scatter-gather.patch new file mode 100644 index 00000000000..5ea56f5f74f --- /dev/null +++ b/target/linux/generic/backport-6.12/625-v7.0-ppp-enable-TX-scatter-gather.patch @@ -0,0 +1,104 @@ +From 42fcb213e58a7da33d5d2d7517b4e521025c68c3 Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Thu, 29 Jan 2026 09:29:02 +0800 +Subject: [PATCH] ppp: enable TX scatter-gather + +PPP channels using chan->direct_xmit prepend the PPP header to a skb and +call dev_queue_xmit() directly. In this mode the skb does not need to be +linear, but the PPP netdevice currently does not advertise +scatter-gather features, causing unnecessary linearization and +preventing GSO. + +Enable NETIF_F_SG and NETIF_F_FRAGLIST on PPP devices. In case a linear +buffer is required (PPP compression, multilink, and channels without +direct_xmit), call skb_linearize() explicitly. + +Signed-off-by: Qingfang Deng +Link: https://patch.msgid.link/20260129012902.941-1-dqfext@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/ppp/ppp_generic.c | 30 +++++++++++++++++++++++++----- + 1 file changed, 25 insertions(+), 5 deletions(-) + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -1632,6 +1632,8 @@ static void ppp_setup(struct net_device + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->priv_destructor = ppp_dev_priv_destructor; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; ++ dev->features = NETIF_F_SG | NETIF_F_FRAGLIST; ++ dev->hw_features = dev->features; + netif_keep_dst(dev); + } + +@@ -1696,6 +1698,10 @@ pad_compress_skb(struct ppp *ppp, struct + ppp->xcomp->comp_extra + ppp->dev->hard_header_len; + int compressor_skb_size = ppp->dev->mtu + + ppp->xcomp->comp_extra + PPP_HDRLEN; ++ ++ if (skb_linearize(skb)) ++ return NULL; ++ + new_skb = alloc_skb(new_skb_size, GFP_ATOMIC); + if (!new_skb) { + if (net_ratelimit()) +@@ -1783,6 +1789,10 @@ ppp_send_frame(struct ppp *ppp, struct s + case PPP_IP: + if (!ppp->vj || (ppp->flags & SC_COMP_TCP) == 0) + break; ++ ++ if (skb_linearize(skb)) ++ goto drop; ++ + /* try to do VJ TCP header compression */ + new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2, + GFP_ATOMIC); +@@ -1880,19 +1890,26 @@ ppp_push(struct ppp *ppp) + } + + if ((ppp->flags & SC_MULTILINK) == 0) { ++ struct ppp_channel *chan; + /* not doing multilink: send it down the first channel */ + list = list->next; + pch = list_entry(list, struct channel, clist); + + spin_lock(&pch->downl); +- if (pch->chan) { +- if (pch->chan->ops->start_xmit(pch->chan, skb)) +- ppp->xmit_pending = NULL; +- } else { +- /* channel got unregistered */ ++ chan = pch->chan; ++ if (unlikely(!chan || (!chan->direct_xmit && skb_linearize(skb)))) { ++ /* channel got unregistered, or it requires a linear ++ * skb but linearization failed ++ */ + kfree_skb(skb); + ppp->xmit_pending = NULL; ++ goto out; + } ++ ++ if (chan->ops->start_xmit(chan, skb)) ++ ppp->xmit_pending = NULL; ++ ++out: + spin_unlock(&pch->downl); + return; + } +@@ -1977,6 +1994,8 @@ static int ppp_mp_explode(struct ppp *pp + return 0; /* can't take now, leave it in xmit_pending */ + + /* Do protocol field compression */ ++ if (skb_linearize(skb)) ++ goto err_linearize; + p = skb->data; + len = skb->len; + if (*p == 0 && mp_protocol_compress) { +@@ -2135,6 +2154,7 @@ static int ppp_mp_explode(struct ppp *pp + + noskb: + spin_unlock(&pch->downl); ++ err_linearize: + if (ppp->debug & 1) + netdev_err(ppp->dev, "PPP: no memory (fragment)\n"); + ++ppp->dev->stats.tx_errors; diff --git a/target/linux/generic/backport-6.12/626-01-v6.13-net-pse-pd-Add-power-limit-check.patch b/target/linux/generic/backport-6.12/626-01-v6.13-net-pse-pd-Add-power-limit-check.patch new file mode 100644 index 00000000000..fbcc3ec3e1d --- /dev/null +++ b/target/linux/generic/backport-6.12/626-01-v6.13-net-pse-pd-Add-power-limit-check.patch @@ -0,0 +1,41 @@ +From 6e56a6d47a7fad705a1a1d088237b0858c01a770 Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Fri, 10 Jan 2025 10:40:22 +0100 +Subject: [PATCH] net: pse-pd: Add power limit check + +Checking only the current limit is not sufficient. According to the +standard, voltage can reach up to 57V and current up to 1.92A, which +exceeds the power limit described in the standard (99.9W). Add a power +limit check to prevent this. + +Acked-by: Oleksij Rempel +Signed-off-by: Kory Maincent +Signed-off-by: Paolo Abeni +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pse_core.c | 3 +++ + include/linux/pse-pd/pse.h | 2 ++ + 2 files changed, 5 insertions(+) +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -868,6 +868,9 @@ int pse_ethtool_set_pw_limit(struct pse_ + int uV, uA, ret; + s64 tmp_64; + ++ if (pw_limit > MAX_PI_PW) ++ return -ERANGE; ++ + ret = regulator_get_voltage(psec->ps); + if (!ret) { + NL_SET_ERR_MSG(extack, +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -11,6 +11,8 @@ + + /* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */ + #define MAX_PI_CURRENT 1920000 ++/* Maximum power in mW according to IEEE 802.3-2022 Table 145-16 */ ++#define MAX_PI_PW 99900 + + struct phy_device; + struct pse_controller_dev; diff --git a/target/linux/generic/backport-6.12/626-02-v6.13-net-pse-pd-tps23881-Simplify-function-returns.patch b/target/linux/generic/backport-6.12/626-02-v6.13-net-pse-pd-tps23881-Simplify-function-returns.patch new file mode 100644 index 00000000000..fc6d5322981 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-02-v6.13-net-pse-pd-tps23881-Simplify-function-returns.patch @@ -0,0 +1,112 @@ +From 0b567519d1152de52b29b2da2c47aa0f39a46266 Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Fri, 10 Jan 2025 10:40:23 +0100 +Subject: [PATCH] net: pse-pd: tps23881: Simplify function returns by removing + redundant checks + +Cleaned up several functions in tps23881 by removing redundant checks on +return values at the end of functions. These check has been removed, and +the return statement now directly returns the function result, reducing +the code's complexity and making it more concise. + +Reviewed-by: Andrew Lunn +Acked-by: Oleksij Rempel +Reviewed-by: Kyle Swenson +Signed-off-by: Kory Maincent +Signed-off-by: Paolo Abeni +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/tps23881.c | 34 ++++++---------------------------- + 1 file changed, 6 insertions(+), 28 deletions(-) +--- a/drivers/net/pse-pd/tps23881.c ++++ b/drivers/net/pse-pd/tps23881.c +@@ -59,7 +59,6 @@ static int tps23881_pi_enable(struct pse + struct i2c_client *client = priv->client; + u8 chan; + u16 val; +- int ret; + + if (id >= TPS23881_MAX_CHANS) + return -ERANGE; +@@ -78,11 +77,7 @@ static int tps23881_pi_enable(struct pse + val |= BIT(chan + 4); + } + +- ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); +- if (ret) +- return ret; +- +- return 0; ++ return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); + } + + static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id) +@@ -91,7 +86,6 @@ static int tps23881_pi_disable(struct ps + struct i2c_client *client = priv->client; + u8 chan; + u16 val; +- int ret; + + if (id >= TPS23881_MAX_CHANS) + return -ERANGE; +@@ -110,11 +104,7 @@ static int tps23881_pi_disable(struct ps + val |= BIT(chan + 8); + } + +- ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); +- if (ret) +- return ret; +- +- return 0; ++ return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); + } + + static int tps23881_pi_is_enabled(struct pse_controller_dev *pcdev, int id) +@@ -480,7 +470,7 @@ tps23881_write_port_matrix(struct tps238 + struct i2c_client *client = priv->client; + u8 pi_id, lgcl_chan, hw_chan; + u16 val = 0; +- int i, ret; ++ int i; + + for (i = 0; i < port_cnt; i++) { + pi_id = port_matrix[i].pi_id; +@@ -511,11 +501,7 @@ tps23881_write_port_matrix(struct tps238 + } + + /* Write hardware ports matrix */ +- ret = i2c_smbus_write_word_data(client, TPS23881_REG_PORT_MAP, val); +- if (ret) +- return ret; +- +- return 0; ++ return i2c_smbus_write_word_data(client, TPS23881_REG_PORT_MAP, val); + } + + static int +@@ -564,11 +550,7 @@ tps23881_set_ports_conf(struct tps23881_ + val |= BIT(port_matrix[i].lgcl_chan[1]) | + BIT(port_matrix[i].lgcl_chan[1] + 4); + } +- ret = i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val); +- if (ret) +- return ret; +- +- return 0; ++ return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val); + } + + static int +@@ -594,11 +576,7 @@ tps23881_set_ports_matrix(struct tps2388 + if (ret) + return ret; + +- ret = tps23881_set_ports_conf(priv, port_matrix); +- if (ret) +- return ret; +- +- return 0; ++ return tps23881_set_ports_conf(priv, port_matrix); + } + + static int tps23881_setup_pi_matrix(struct pse_controller_dev *pcdev) diff --git a/target/linux/generic/backport-6.12/626-03-v6.13-net-pse-pd-tps23881-Use-helpers-to-calculate-bit-offset.patch b/target/linux/generic/backport-6.12/626-03-v6.13-net-pse-pd-tps23881-Use-helpers-to-calculate-bit-offset.patch new file mode 100644 index 00000000000..3b390ea5651 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-03-v6.13-net-pse-pd-tps23881-Use-helpers-to-calculate-bit-offset.patch @@ -0,0 +1,190 @@ +From 4c2bab507eb7edc8e497e91b9b7f05d76d7e32bb Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Fri, 10 Jan 2025 10:40:24 +0100 +Subject: [PATCH] net: pse-pd: tps23881: Use helpers to calculate bit offset + for a channel + +This driver frequently follows a pattern where two registers are read or +written in a single operation, followed by calculating the bit offset for +a specific channel. + +Introduce helpers to streamline this process and reduce code redundancy, +making the codebase cleaner and more maintainable. + +Acked-by: Oleksij Rempel +Signed-off-by: Kory Maincent +Signed-off-by: Paolo Abeni +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/tps23881.c | 107 ++++++++++++++++++++++------------ + 1 file changed, 69 insertions(+), 38 deletions(-) +--- a/drivers/net/pse-pd/tps23881.c ++++ b/drivers/net/pse-pd/tps23881.c +@@ -53,6 +53,55 @@ static struct tps23881_priv *to_tps23881 + return container_of(pcdev, struct tps23881_priv, pcdev); + } + ++/* ++ * Helper to extract a value from a u16 register value, which is made of two ++ * u8 registers. The function calculates the bit offset based on the channel ++ * and extracts the relevant bits using a provided field mask. ++ * ++ * @param reg_val: The u16 register value (composed of two u8 registers). ++ * @param chan: The channel number (0-7). ++ * @param field_offset: The base bit offset to apply (e.g., 0 or 4). ++ * @param field_mask: The mask to apply to extract the required bits. ++ * @return: The extracted value for the specific channel. ++ */ ++static u16 tps23881_calc_val(u16 reg_val, u8 chan, u8 field_offset, ++ u16 field_mask) ++{ ++ if (chan >= 4) ++ reg_val >>= 8; ++ ++ return (reg_val >> field_offset) & field_mask; ++} ++ ++/* ++ * Helper to combine individual channel values into a u16 register value. ++ * The function sets the value for a specific channel in the appropriate ++ * position. ++ * ++ * @param reg_val: The current u16 register value. ++ * @param chan: The channel number (0-7). ++ * @param field_offset: The base bit offset to apply (e.g., 0 or 4). ++ * @param field_mask: The mask to apply for the field (e.g., 0x0F). ++ * @param field_val: The value to set for the specific channel (masked by ++ * field_mask). ++ * @return: The updated u16 register value with the channel value set. ++ */ ++static u16 tps23881_set_val(u16 reg_val, u8 chan, u8 field_offset, ++ u16 field_mask, u16 field_val) ++{ ++ field_val &= field_mask; ++ ++ if (chan < 4) { ++ reg_val &= ~(field_mask << field_offset); ++ reg_val |= (field_val << field_offset); ++ } else { ++ reg_val &= ~(field_mask << (field_offset + 8)); ++ reg_val |= (field_val << (field_offset + 8)); ++ } ++ ++ return reg_val; ++} ++ + static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id) + { + struct tps23881_priv *priv = to_tps23881_priv(pcdev); +@@ -64,17 +113,12 @@ static int tps23881_pi_enable(struct pse + return -ERANGE; + + chan = priv->port[id].chan[0]; +- if (chan < 4) +- val = BIT(chan); +- else +- val = BIT(chan + 4); ++ val = tps23881_set_val(0, chan, 0, BIT(chan % 4), BIT(chan % 4)); + + if (priv->port[id].is_4p) { + chan = priv->port[id].chan[1]; +- if (chan < 4) +- val |= BIT(chan); +- else +- val |= BIT(chan + 4); ++ val = tps23881_set_val(val, chan, 0, BIT(chan % 4), ++ BIT(chan % 4)); + } + + return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); +@@ -91,17 +135,12 @@ static int tps23881_pi_disable(struct ps + return -ERANGE; + + chan = priv->port[id].chan[0]; +- if (chan < 4) +- val = BIT(chan + 4); +- else +- val = BIT(chan + 8); ++ val = tps23881_set_val(0, chan, 4, BIT(chan % 4), BIT(chan % 4)); + + if (priv->port[id].is_4p) { + chan = priv->port[id].chan[1]; +- if (chan < 4) +- val |= BIT(chan + 4); +- else +- val |= BIT(chan + 8); ++ val = tps23881_set_val(val, chan, 4, BIT(chan % 4), ++ BIT(chan % 4)); + } + + return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); +@@ -113,6 +152,7 @@ static int tps23881_pi_is_enabled(struct + struct i2c_client *client = priv->client; + bool enabled; + u8 chan; ++ u16 val; + int ret; + + ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS); +@@ -120,17 +160,13 @@ static int tps23881_pi_is_enabled(struct + return ret; + + chan = priv->port[id].chan[0]; +- if (chan < 4) +- enabled = ret & BIT(chan); +- else +- enabled = ret & BIT(chan + 4); ++ val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4)); ++ enabled = !!(val); + + if (priv->port[id].is_4p) { + chan = priv->port[id].chan[1]; +- if (chan < 4) +- enabled &= !!(ret & BIT(chan)); +- else +- enabled &= !!(ret & BIT(chan + 4)); ++ val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4)); ++ enabled &= !!(val); + } + + /* Return enabled status only if both channel are on this state */ +@@ -146,6 +182,7 @@ static int tps23881_ethtool_get_status(s + struct i2c_client *client = priv->client; + bool enabled, delivering; + u8 chan; ++ u16 val; + int ret; + + ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS); +@@ -153,23 +190,17 @@ static int tps23881_ethtool_get_status(s + return ret; + + chan = priv->port[id].chan[0]; +- if (chan < 4) { +- enabled = ret & BIT(chan); +- delivering = ret & BIT(chan + 4); +- } else { +- enabled = ret & BIT(chan + 4); +- delivering = ret & BIT(chan + 8); +- } ++ val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4)); ++ enabled = !!(val); ++ val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4)); ++ delivering = !!(val); + + if (priv->port[id].is_4p) { + chan = priv->port[id].chan[1]; +- if (chan < 4) { +- enabled &= !!(ret & BIT(chan)); +- delivering &= !!(ret & BIT(chan + 4)); +- } else { +- enabled &= !!(ret & BIT(chan + 4)); +- delivering &= !!(ret & BIT(chan + 8)); +- } ++ val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4)); ++ enabled &= !!(val); ++ val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4)); ++ delivering &= !!(val); + } + + /* Return delivering status only if both channel are on this state */ diff --git a/target/linux/generic/backport-6.12/626-04-v6.13-net-pse-pd-tps23881-Add-missing-configuration-register.patch b/target/linux/generic/backport-6.12/626-04-v6.13-net-pse-pd-tps23881-Add-missing-configuration-register.patch new file mode 100644 index 00000000000..c00dd9050ff --- /dev/null +++ b/target/linux/generic/backport-6.12/626-04-v6.13-net-pse-pd-tps23881-Add-missing-configuration-register.patch @@ -0,0 +1,63 @@ +From f3cb3c7bea0c08e821d8e9dfd2f96acd1db7c24e Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Fri, 10 Jan 2025 10:40:25 +0100 +Subject: [PATCH] net: pse-pd: tps23881: Add missing configuration register + after disable + +When setting the PWOFF register, the controller resets multiple +configuration registers. This patch ensures these registers are +reconfigured as needed following a disable operation. + +Acked-by: Oleksij Rempel +Signed-off-by: Kory Maincent +Signed-off-by: Paolo Abeni +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/tps23881.c | 30 +++++++++++++++++++++++++++++- + 1 file changed, 29 insertions(+), 1 deletion(-) +--- a/drivers/net/pse-pd/tps23881.c ++++ b/drivers/net/pse-pd/tps23881.c +@@ -130,6 +130,7 @@ static int tps23881_pi_disable(struct ps + struct i2c_client *client = priv->client; + u8 chan; + u16 val; ++ int ret; + + if (id >= TPS23881_MAX_CHANS) + return -ERANGE; +@@ -143,7 +144,34 @@ static int tps23881_pi_disable(struct ps + BIT(chan % 4)); + } + +- return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); ++ ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); ++ if (ret) ++ return ret; ++ ++ /* PWOFF command resets lots of register which need to be ++ * configured again. According to the datasheet "It may take upwards ++ * of 5ms after PWOFFn command for all register values to be updated" ++ */ ++ mdelay(5); ++ ++ /* Enable detection and classification */ ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_DET_CLA_EN); ++ if (ret < 0) ++ return ret; ++ ++ chan = priv->port[id].chan[0]; ++ val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4)); ++ val = tps23881_set_val(val, chan, 4, BIT(chan % 4), BIT(chan % 4)); ++ ++ if (priv->port[id].is_4p) { ++ chan = priv->port[id].chan[1]; ++ val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), ++ BIT(chan % 4)); ++ val = tps23881_set_val(val, chan, 4, BIT(chan % 4), ++ BIT(chan % 4)); ++ } ++ ++ return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val); + } + + static int tps23881_pi_is_enabled(struct pse_controller_dev *pcdev, int id) diff --git a/target/linux/generic/backport-6.12/626-05-v6.13-net-pse-pd-Split-ethtool_get_status-into-multiple-callbacks.patch b/target/linux/generic/backport-6.12/626-05-v6.13-net-pse-pd-Split-ethtool_get_status-into-multiple-callbacks.patch new file mode 100644 index 00000000000..71a2379e3b4 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-05-v6.13-net-pse-pd-Split-ethtool_get_status-into-multiple-callbacks.patch @@ -0,0 +1,714 @@ +From 3e9dbfec499807767d03592ebdf19d9c15fd495b Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Fri, 10 Jan 2025 10:40:27 +0100 +Subject: [PATCH] net: pse-pd: Split ethtool_get_status into multiple callbacks + +The ethtool_get_status callback currently handles all status and PSE +information within a single function. This approach has two key +drawbacks: + +1. If the core requires some information for purposes other than + ethtool_get_status, redundant code will be needed to fetch the same + data from the driver (like is_enabled). + +2. Drivers currently have access to all information passed to ethtool. + New variables will soon be added to ethtool status, such as PSE ID, + power domain IDs, and budget evaluation strategies, which are meant + to be managed solely by the core. Drivers should not have the ability + to modify these variables. + +To resolve these issues, ethtool_get_status has been split into multiple +callbacks, with each handling a specific piece of information required +by ethtool or the core. + +Signed-off-by: Kory Maincent +Signed-off-by: Paolo Abeni +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pd692x0.c | 153 ++++++++++++++++++++--------- + drivers/net/pse-pd/pse_core.c | 83 ++++++++++++++-- + drivers/net/pse-pd/pse_regulator.c | 26 +++-- + drivers/net/pse-pd/tps23881.c | 60 ++++++++--- + include/linux/pse-pd/pse.h | 87 +++++++++++++--- + net/ethtool/pse-pd.c | 8 +- + 6 files changed, 323 insertions(+), 94 deletions(-) + +--- a/drivers/net/pse-pd/pd692x0.c ++++ b/drivers/net/pse-pd/pd692x0.c +@@ -517,21 +517,38 @@ pd692x0_pse_ext_state_map[] = { + { /* sentinel */ } + }; + +-static void +-pd692x0_get_ext_state(struct ethtool_c33_pse_ext_state_info *c33_ext_state_info, +- u32 status_code) ++static int ++pd692x0_pi_get_ext_state(struct pse_controller_dev *pcdev, int id, ++ struct pse_ext_state_info *ext_state_info) + { ++ struct ethtool_c33_pse_ext_state_info *c33_ext_state_info; + const struct pd692x0_pse_ext_state_mapping *ext_state_map; ++ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); ++ struct pd692x0_msg msg, buf = {0}; ++ int ret; ++ ++ ret = pd692x0_fw_unavailable(priv); ++ if (ret) ++ return ret; + ++ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS]; ++ msg.sub[2] = id; ++ ret = pd692x0_sendrecv_msg(priv, &msg, &buf); ++ if (ret < 0) ++ return ret; ++ ++ c33_ext_state_info = &ext_state_info->c33_ext_state_info; + ext_state_map = pd692x0_pse_ext_state_map; + while (ext_state_map->status_code) { +- if (ext_state_map->status_code == status_code) { ++ if (ext_state_map->status_code == buf.sub[0]) { + c33_ext_state_info->c33_pse_ext_state = ext_state_map->pse_ext_state; + c33_ext_state_info->__c33_pse_ext_substate = ext_state_map->pse_ext_substate; +- return; ++ return 0; + } + ext_state_map++; + } ++ ++ return 0; + } + + struct pd692x0_class_pw { +@@ -613,35 +630,36 @@ static int pd692x0_pi_set_pw_from_table( + } + + static int +-pd692x0_pi_get_pw_ranges(struct pse_control_status *st) ++pd692x0_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id, ++ struct pse_pw_limit_ranges *pw_limit_ranges) + { ++ struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; + const struct pd692x0_class_pw *pw_table; + int i; + + pw_table = pd692x0_class_pw_table; +- st->c33_pw_limit_ranges = kcalloc(PD692X0_CLASS_PW_TABLE_SIZE, +- sizeof(struct ethtool_c33_pse_pw_limit_range), +- GFP_KERNEL); +- if (!st->c33_pw_limit_ranges) ++ c33_pw_limit_ranges = kcalloc(PD692X0_CLASS_PW_TABLE_SIZE, ++ sizeof(*c33_pw_limit_ranges), ++ GFP_KERNEL); ++ if (!c33_pw_limit_ranges) + return -ENOMEM; + + for (i = 0; i < PD692X0_CLASS_PW_TABLE_SIZE; i++, pw_table++) { +- st->c33_pw_limit_ranges[i].min = pw_table->class_pw; +- st->c33_pw_limit_ranges[i].max = pw_table->class_pw + pw_table->max_added_class_pw; ++ c33_pw_limit_ranges[i].min = pw_table->class_pw; ++ c33_pw_limit_ranges[i].max = pw_table->class_pw + ++ pw_table->max_added_class_pw; + } + +- st->c33_pw_limit_nb_ranges = i; +- return 0; ++ pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges; ++ return i; + } + +-static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev, +- unsigned long id, +- struct netlink_ext_ack *extack, +- struct pse_control_status *status) ++static int ++pd692x0_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, ++ struct pse_admin_state *admin_state) + { + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); + struct pd692x0_msg msg, buf = {0}; +- u32 class; + int ret; + + ret = pd692x0_fw_unavailable(priv); +@@ -654,39 +672,65 @@ static int pd692x0_ethtool_get_status(st + if (ret < 0) + return ret; + +- /* Compare Port Status (Communication Protocol Document par. 7.1) */ +- if ((buf.sub[0] & 0xf0) == 0x80 || (buf.sub[0] & 0xf0) == 0x90) +- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; +- else if (buf.sub[0] == 0x1b || buf.sub[0] == 0x22) +- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING; +- else if (buf.sub[0] == 0x12) +- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_FAULT; +- else +- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; +- + if (buf.sub[1]) +- status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; + else +- status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; + +- priv->admin_state[id] = status->c33_admin_state; ++ priv->admin_state[id] = admin_state->c33_admin_state; + +- pd692x0_get_ext_state(&status->c33_ext_state_info, buf.sub[0]); +- status->c33_actual_pw = (buf.data[0] << 4 | buf.data[1]) * 100; ++ return 0; ++} + +- msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_PARAM]; ++static int ++pd692x0_pi_get_pw_status(struct pse_controller_dev *pcdev, int id, ++ struct pse_pw_status *pw_status) ++{ ++ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); ++ struct pd692x0_msg msg, buf = {0}; ++ int ret; ++ ++ ret = pd692x0_fw_unavailable(priv); ++ if (ret) ++ return ret; ++ ++ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS]; + msg.sub[2] = id; +- memset(&buf, 0, sizeof(buf)); + ret = pd692x0_sendrecv_msg(priv, &msg, &buf); + if (ret < 0) + return ret; + +- ret = pd692x0_pi_get_pw_from_table(buf.data[0], buf.data[1]); +- if (ret < 0) ++ /* Compare Port Status (Communication Protocol Document par. 7.1) */ ++ if ((buf.sub[0] & 0xf0) == 0x80 || (buf.sub[0] & 0xf0) == 0x90) ++ pw_status->c33_pw_status = ++ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; ++ else if (buf.sub[0] == 0x1b || buf.sub[0] == 0x22) ++ pw_status->c33_pw_status = ++ ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING; ++ else if (buf.sub[0] == 0x12) ++ pw_status->c33_pw_status = ++ ETHTOOL_C33_PSE_PW_D_STATUS_FAULT; ++ else ++ pw_status->c33_pw_status = ++ ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; ++ ++ return 0; ++} ++ ++static int ++pd692x0_pi_get_pw_class(struct pse_controller_dev *pcdev, int id) ++{ ++ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); ++ struct pd692x0_msg msg, buf = {0}; ++ u32 class; ++ int ret; ++ ++ ret = pd692x0_fw_unavailable(priv); ++ if (ret) + return ret; +- status->c33_avail_pw_limit = ret; + +- memset(&buf, 0, sizeof(buf)); + msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_CLASS]; + msg.sub[2] = id; + ret = pd692x0_sendrecv_msg(priv, &msg, &buf); +@@ -695,13 +739,29 @@ static int pd692x0_ethtool_get_status(st + + class = buf.data[3] >> 4; + if (class <= 8) +- status->c33_pw_class = class; ++ return class; ++ ++ return 0; ++} ++ ++static int ++pd692x0_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id) ++{ ++ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); ++ struct pd692x0_msg msg, buf = {0}; ++ int ret; ++ ++ ret = pd692x0_fw_unavailable(priv); ++ if (ret) ++ return ret; + +- ret = pd692x0_pi_get_pw_ranges(status); ++ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS]; ++ msg.sub[2] = id; ++ ret = pd692x0_sendrecv_msg(priv, &msg, &buf); + if (ret < 0) + return ret; + +- return 0; ++ return (buf.data[0] << 4 | buf.data[1]) * 100; + } + + static struct pd692x0_msg_ver pd692x0_get_sw_version(struct pd692x0_priv *priv) +@@ -1038,13 +1098,18 @@ static int pd692x0_pi_set_pw_limit(struc + + static const struct pse_controller_ops pd692x0_ops = { + .setup_pi_matrix = pd692x0_setup_pi_matrix, +- .ethtool_get_status = pd692x0_ethtool_get_status, ++ .pi_get_admin_state = pd692x0_pi_get_admin_state, ++ .pi_get_pw_status = pd692x0_pi_get_pw_status, ++ .pi_get_ext_state = pd692x0_pi_get_ext_state, ++ .pi_get_pw_class = pd692x0_pi_get_pw_class, ++ .pi_get_actual_pw = pd692x0_pi_get_actual_pw, + .pi_enable = pd692x0_pi_enable, + .pi_disable = pd692x0_pi_disable, + .pi_is_enabled = pd692x0_pi_is_enabled, + .pi_get_voltage = pd692x0_pi_get_voltage, + .pi_get_pw_limit = pd692x0_pi_get_pw_limit, + .pi_set_pw_limit = pd692x0_pi_set_pw_limit, ++ .pi_get_pw_limit_ranges = pd692x0_pi_get_pw_limit_ranges, + }; + + #define PD692X0_FW_LINE_MAX_SZ 0xff +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -443,6 +443,13 @@ int pse_controller_register(struct pse_c + if (!pcdev->nr_lines) + pcdev->nr_lines = 1; + ++ if (!pcdev->ops->pi_get_admin_state || ++ !pcdev->ops->pi_get_pw_status) { ++ dev_err(pcdev->dev, ++ "Mandatory status report callbacks are missing"); ++ return -EINVAL; ++ } ++ + ret = of_load_pse_pis(pcdev); + if (ret) + return ret; +@@ -745,25 +752,81 @@ EXPORT_SYMBOL_GPL(of_pse_control_get); + */ + int pse_ethtool_get_status(struct pse_control *psec, + struct netlink_ext_ack *extack, +- struct pse_control_status *status) ++ struct ethtool_pse_control_status *status) + { ++ struct pse_admin_state admin_state = {0}; ++ struct pse_pw_status pw_status = {0}; + const struct pse_controller_ops *ops; + struct pse_controller_dev *pcdev; +- int err; ++ int ret; + + pcdev = psec->pcdev; + ops = pcdev->ops; +- if (!ops->ethtool_get_status) { +- NL_SET_ERR_MSG(extack, +- "PSE driver does not support status report"); +- return -EOPNOTSUPP; ++ mutex_lock(&pcdev->lock); ++ ret = ops->pi_get_admin_state(pcdev, psec->id, &admin_state); ++ if (ret) ++ goto out; ++ status->podl_admin_state = admin_state.podl_admin_state; ++ status->c33_admin_state = admin_state.c33_admin_state; ++ ++ ret = ops->pi_get_pw_status(pcdev, psec->id, &pw_status); ++ if (ret) ++ goto out; ++ status->podl_pw_status = pw_status.podl_pw_status; ++ status->c33_pw_status = pw_status.c33_pw_status; ++ ++ if (ops->pi_get_ext_state) { ++ struct pse_ext_state_info ext_state_info = {0}; ++ ++ ret = ops->pi_get_ext_state(pcdev, psec->id, ++ &ext_state_info); ++ if (ret) ++ goto out; ++ ++ memcpy(&status->c33_ext_state_info, ++ &ext_state_info.c33_ext_state_info, ++ sizeof(status->c33_ext_state_info)); + } + +- mutex_lock(&pcdev->lock); +- err = ops->ethtool_get_status(pcdev, psec->id, extack, status); +- mutex_unlock(&pcdev->lock); ++ if (ops->pi_get_pw_class) { ++ ret = ops->pi_get_pw_class(pcdev, psec->id); ++ if (ret < 0) ++ goto out; ++ ++ status->c33_pw_class = ret; ++ } ++ ++ if (ops->pi_get_actual_pw) { ++ ret = ops->pi_get_actual_pw(pcdev, psec->id); ++ if (ret < 0) ++ goto out; ++ ++ status->c33_actual_pw = ret; ++ } ++ ++ if (ops->pi_get_pw_limit) { ++ ret = ops->pi_get_pw_limit(pcdev, psec->id); ++ if (ret < 0) ++ goto out; ++ ++ status->c33_avail_pw_limit = ret; ++ } ++ ++ if (ops->pi_get_pw_limit_ranges) { ++ struct pse_pw_limit_ranges pw_limit_ranges = {0}; + +- return err; ++ ret = ops->pi_get_pw_limit_ranges(pcdev, psec->id, ++ &pw_limit_ranges); ++ if (ret < 0) ++ goto out; ++ ++ status->c33_pw_limit_ranges = ++ pw_limit_ranges.c33_pw_limit_ranges; ++ status->c33_pw_limit_nb_ranges = ret; ++ } ++out: ++ mutex_unlock(&psec->pcdev->lock); ++ return ret; + } + EXPORT_SYMBOL_GPL(pse_ethtool_get_status); + +--- a/drivers/net/pse-pd/pse_regulator.c ++++ b/drivers/net/pse-pd/pse_regulator.c +@@ -60,9 +60,19 @@ pse_reg_pi_is_enabled(struct pse_control + } + + static int +-pse_reg_ethtool_get_status(struct pse_controller_dev *pcdev, unsigned long id, +- struct netlink_ext_ack *extack, +- struct pse_control_status *status) ++pse_reg_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, ++ struct pse_admin_state *admin_state) ++{ ++ struct pse_reg_priv *priv = to_pse_reg(pcdev); ++ ++ admin_state->podl_admin_state = priv->admin_state; ++ ++ return 0; ++} ++ ++static int ++pse_reg_pi_get_pw_status(struct pse_controller_dev *pcdev, int id, ++ struct pse_pw_status *pw_status) + { + struct pse_reg_priv *priv = to_pse_reg(pcdev); + int ret; +@@ -72,18 +82,18 @@ pse_reg_ethtool_get_status(struct pse_co + return ret; + + if (!ret) +- status->podl_pw_status = ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED; ++ pw_status->podl_pw_status = ++ ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED; + else +- status->podl_pw_status = ++ pw_status->podl_pw_status = + ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING; + +- status->podl_admin_state = priv->admin_state; +- + return 0; + } + + static const struct pse_controller_ops pse_reg_ops = { +- .ethtool_get_status = pse_reg_ethtool_get_status, ++ .pi_get_admin_state = pse_reg_pi_get_admin_state, ++ .pi_get_pw_status = pse_reg_pi_get_pw_status, + .pi_enable = pse_reg_pi_enable, + .pi_is_enabled = pse_reg_pi_is_enabled, + .pi_disable = pse_reg_pi_disable, +--- a/drivers/net/pse-pd/tps23881.c ++++ b/drivers/net/pse-pd/tps23881.c +@@ -201,14 +201,13 @@ static int tps23881_pi_is_enabled(struct + return enabled; + } + +-static int tps23881_ethtool_get_status(struct pse_controller_dev *pcdev, +- unsigned long id, +- struct netlink_ext_ack *extack, +- struct pse_control_status *status) ++static int ++tps23881_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, ++ struct pse_admin_state *admin_state) + { + struct tps23881_priv *priv = to_tps23881_priv(pcdev); + struct i2c_client *client = priv->client; +- bool enabled, delivering; ++ bool enabled; + u8 chan; + u16 val; + int ret; +@@ -220,28 +219,56 @@ static int tps23881_ethtool_get_status(s + chan = priv->port[id].chan[0]; + val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4)); + enabled = !!(val); +- val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4)); +- delivering = !!(val); + + if (priv->port[id].is_4p) { + chan = priv->port[id].chan[1]; + val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4)); + enabled &= !!(val); ++ } ++ ++ /* Return enabled status only if both channel are on this state */ ++ if (enabled) ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; ++ else ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; ++ ++ return 0; ++} ++ ++static int ++tps23881_pi_get_pw_status(struct pse_controller_dev *pcdev, int id, ++ struct pse_pw_status *pw_status) ++{ ++ struct tps23881_priv *priv = to_tps23881_priv(pcdev); ++ struct i2c_client *client = priv->client; ++ bool delivering; ++ u8 chan; ++ u16 val; ++ int ret; ++ ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS); ++ if (ret < 0) ++ return ret; ++ ++ chan = priv->port[id].chan[0]; ++ val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4)); ++ delivering = !!(val); ++ ++ if (priv->port[id].is_4p) { ++ chan = priv->port[id].chan[1]; + val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4)); + delivering &= !!(val); + } + + /* Return delivering status only if both channel are on this state */ + if (delivering) +- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; +- else +- status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; +- +- /* Return enabled status only if both channel are on this state */ +- if (enabled) +- status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; ++ pw_status->c33_pw_status = ++ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; + else +- status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; ++ pw_status->c33_pw_status = ++ ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; + + return 0; + } +@@ -664,7 +691,8 @@ static const struct pse_controller_ops t + .pi_enable = tps23881_pi_enable, + .pi_disable = tps23881_pi_disable, + .pi_is_enabled = tps23881_pi_is_enabled, +- .ethtool_get_status = tps23881_ethtool_get_status, ++ .pi_get_admin_state = tps23881_pi_get_admin_state, ++ .pi_get_pw_status = tps23881_pi_get_pw_status, + }; + + static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin"; +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -31,7 +31,52 @@ struct pse_control_config { + }; + + /** +- * struct pse_control_status - PSE control/channel status. ++ * struct pse_admin_state - PSE operational state ++ * ++ * @podl_admin_state: operational state of the PoDL PSE ++ * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState ++ * @c33_admin_state: operational state of the PSE ++ * functions. IEEE 802.3-2022 30.9.1.1.2 aPSEAdminState ++ */ ++struct pse_admin_state { ++ enum ethtool_podl_pse_admin_state podl_admin_state; ++ enum ethtool_c33_pse_admin_state c33_admin_state; ++}; ++ ++/** ++ * struct pse_pw_status - PSE power detection status ++ * ++ * @podl_pw_status: power detection status of the PoDL PSE. ++ * IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus: ++ * @c33_pw_status: power detection status of the PSE. ++ * IEEE 802.3-2022 30.9.1.1.5 aPSEPowerDetectionStatus: ++ */ ++struct pse_pw_status { ++ enum ethtool_podl_pse_pw_d_status podl_pw_status; ++ enum ethtool_c33_pse_pw_d_status c33_pw_status; ++}; ++ ++/** ++ * struct pse_ext_state_info - PSE extended state information ++ * ++ * @c33_ext_state_info: extended state information of the PSE ++ */ ++struct pse_ext_state_info { ++ struct ethtool_c33_pse_ext_state_info c33_ext_state_info; ++}; ++ ++/** ++ * struct pse_pw_limit_ranges - PSE power limit configuration range ++ * ++ * @c33_pw_limit_ranges: supported power limit configuration range. The driver ++ * is in charge of the memory allocation. ++ */ ++struct pse_pw_limit_ranges { ++ struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; ++}; ++ ++/** ++ * struct ethtool_pse_control_status - PSE control/channel status. + * + * @podl_admin_state: operational state of the PoDL PSE + * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState +@@ -49,11 +94,11 @@ struct pse_control_config { + * @c33_avail_pw_limit: available power limit of the PSE in mW + * IEEE 802.3-2022 145.2.5.4 pse_avail_pwr + * @c33_pw_limit_ranges: supported power limit configuration range. The driver +- * is in charge of the memory allocation. ++ * is in charge of the memory allocation + * @c33_pw_limit_nb_ranges: number of supported power limit configuration + * ranges + */ +-struct pse_control_status { ++struct ethtool_pse_control_status { + enum ethtool_podl_pse_admin_state podl_admin_state; + enum ethtool_podl_pse_pw_d_status podl_pw_status; + enum ethtool_c33_pse_admin_state c33_admin_state; +@@ -69,22 +114,37 @@ struct pse_control_status { + /** + * struct pse_controller_ops - PSE controller driver callbacks + * +- * @ethtool_get_status: get PSE control status for ethtool interface + * @setup_pi_matrix: setup PI matrix of the PSE controller ++ * @pi_get_admin_state: Get the operational state of the PSE PI. This ops ++ * is mandatory. ++ * @pi_get_pw_status: Get the power detection status of the PSE PI. This ++ * ops is mandatory. ++ * @pi_get_ext_state: Get the extended state of the PSE PI. ++ * @pi_get_pw_class: Get the power class of the PSE PI. ++ * @pi_get_actual_pw: Get actual power of the PSE PI in mW. + * @pi_is_enabled: Return 1 if the PSE PI is enabled, 0 if not. + * May also return negative errno. + * @pi_enable: Configure the PSE PI as enabled. + * @pi_disable: Configure the PSE PI as disabled. + * @pi_get_voltage: Return voltage similarly to get_voltage regulator +- * callback. +- * @pi_get_pw_limit: Get the configured power limit of the PSE PI. +- * @pi_set_pw_limit: Configure the power limit of the PSE PI. ++ * callback in uV. ++ * @pi_get_pw_limit: Get the configured power limit of the PSE PI in mW. ++ * @pi_set_pw_limit: Configure the power limit of the PSE PI in mW. ++ * @pi_get_pw_limit_ranges: Get the supported power limit configuration ++ * range. The driver is in charge of the memory ++ * allocation and should return the number of ++ * ranges. + */ + struct pse_controller_ops { +- int (*ethtool_get_status)(struct pse_controller_dev *pcdev, +- unsigned long id, struct netlink_ext_ack *extack, +- struct pse_control_status *status); + int (*setup_pi_matrix)(struct pse_controller_dev *pcdev); ++ int (*pi_get_admin_state)(struct pse_controller_dev *pcdev, int id, ++ struct pse_admin_state *admin_state); ++ int (*pi_get_pw_status)(struct pse_controller_dev *pcdev, int id, ++ struct pse_pw_status *pw_status); ++ int (*pi_get_ext_state)(struct pse_controller_dev *pcdev, int id, ++ struct pse_ext_state_info *ext_state_info); ++ int (*pi_get_pw_class)(struct pse_controller_dev *pcdev, int id); ++ int (*pi_get_actual_pw)(struct pse_controller_dev *pcdev, int id); + int (*pi_is_enabled)(struct pse_controller_dev *pcdev, int id); + int (*pi_enable)(struct pse_controller_dev *pcdev, int id); + int (*pi_disable)(struct pse_controller_dev *pcdev, int id); +@@ -93,12 +153,15 @@ struct pse_controller_ops { + int id); + int (*pi_set_pw_limit)(struct pse_controller_dev *pcdev, + int id, int max_mW); ++ int (*pi_get_pw_limit_ranges)(struct pse_controller_dev *pcdev, int id, ++ struct pse_pw_limit_ranges *pw_limit_ranges); + }; + + struct module; + struct device_node; + struct of_phandle_args; + struct pse_control; ++struct ethtool_pse_control_status; + + /* PSE PI pairset pinout can either be Alternative A or Alternative B */ + enum pse_pi_pairset_pinout { +@@ -175,7 +238,7 @@ void pse_control_put(struct pse_control + + int pse_ethtool_get_status(struct pse_control *psec, + struct netlink_ext_ack *extack, +- struct pse_control_status *status); ++ struct ethtool_pse_control_status *status); + int pse_ethtool_set_config(struct pse_control *psec, + struct netlink_ext_ack *extack, + const struct pse_control_config *config); +@@ -201,7 +264,7 @@ static inline void pse_control_put(struc + + static inline int pse_ethtool_get_status(struct pse_control *psec, + struct netlink_ext_ack *extack, +- struct pse_control_status *status) ++ struct ethtool_pse_control_status *status) + { + return -EOPNOTSUPP; + } +--- a/net/ethtool/pse-pd.c ++++ b/net/ethtool/pse-pd.c +@@ -19,7 +19,7 @@ struct pse_req_info { + + struct pse_reply_data { + struct ethnl_reply_data base; +- struct pse_control_status status; ++ struct ethtool_pse_control_status status; + }; + + #define PSE_REPDATA(__reply_base) \ +@@ -80,7 +80,7 @@ static int pse_reply_size(const struct e + const struct ethnl_reply_data *reply_base) + { + const struct pse_reply_data *data = PSE_REPDATA(reply_base); +- const struct pse_control_status *st = &data->status; ++ const struct ethtool_pse_control_status *st = &data->status; + int len = 0; + + if (st->podl_admin_state > 0) +@@ -114,7 +114,7 @@ static int pse_reply_size(const struct e + } + + static int pse_put_pw_limit_ranges(struct sk_buff *skb, +- const struct pse_control_status *st) ++ const struct ethtool_pse_control_status *st) + { + const struct ethtool_c33_pse_pw_limit_range *pw_limit_ranges; + int i; +@@ -146,7 +146,7 @@ static int pse_fill_reply(struct sk_buff + const struct ethnl_reply_data *reply_base) + { + const struct pse_reply_data *data = PSE_REPDATA(reply_base); +- const struct pse_control_status *st = &data->status; ++ const struct ethtool_pse_control_status *st = &data->status; + + if (st->podl_admin_state > 0 && + nla_put_u32(skb, ETHTOOL_A_PODL_PSE_ADMIN_STATE, diff --git a/target/linux/generic/backport-6.12/626-06-v6.13-net-pse-pd-Remove-is_enabled-callback-from-drivers.patch b/target/linux/generic/backport-6.12/626-06-v6.13-net-pse-pd-Remove-is_enabled-callback-from-drivers.patch new file mode 100644 index 00000000000..dd7a20efaa9 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-06-v6.13-net-pse-pd-Remove-is_enabled-callback-from-drivers.patch @@ -0,0 +1,184 @@ +From 4640a1f0d8f2246f34d6e74330d7e7d2cf75605b Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Fri, 10 Jan 2025 10:40:28 +0100 +Subject: [PATCH] net: pse-pd: Remove is_enabled callback from drivers + +The is_enabled callback is now redundant as the admin_state can be obtained +directly from the driver and provides the same information. + +To simplify functionality, the core will handle this internally, making +the is_enabled callback unnecessary at the driver level. Remove the +callback from all drivers. + +Acked-by: Oleksij Rempel +Signed-off-by: Kory Maincent +Signed-off-by: Paolo Abeni +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pd692x0.c | 26 -------------------------- + drivers/net/pse-pd/pse_core.c | 13 +++++++++++-- + drivers/net/pse-pd/pse_regulator.c | 9 --------- + drivers/net/pse-pd/tps23881.c | 28 ---------------------------- + include/linux/pse-pd/pse.h | 3 --- + 5 files changed, 11 insertions(+), 68 deletions(-) + +--- a/drivers/net/pse-pd/pd692x0.c ++++ b/drivers/net/pse-pd/pd692x0.c +@@ -431,31 +431,6 @@ static int pd692x0_pi_disable(struct pse + return 0; + } + +-static int pd692x0_pi_is_enabled(struct pse_controller_dev *pcdev, int id) +-{ +- struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); +- struct pd692x0_msg msg, buf = {0}; +- int ret; +- +- ret = pd692x0_fw_unavailable(priv); +- if (ret) +- return ret; +- +- msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS]; +- msg.sub[2] = id; +- ret = pd692x0_sendrecv_msg(priv, &msg, &buf); +- if (ret < 0) +- return ret; +- +- if (buf.sub[1]) { +- priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; +- return 1; +- } else { +- priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; +- return 0; +- } +-} +- + struct pd692x0_pse_ext_state_mapping { + u32 status_code; + enum ethtool_c33_pse_ext_state pse_ext_state; +@@ -1105,7 +1080,6 @@ static const struct pse_controller_ops p + .pi_get_actual_pw = pd692x0_pi_get_actual_pw, + .pi_enable = pd692x0_pi_enable, + .pi_disable = pd692x0_pi_disable, +- .pi_is_enabled = pd692x0_pi_is_enabled, + .pi_get_voltage = pd692x0_pi_get_voltage, + .pi_get_pw_limit = pd692x0_pi_get_pw_limit, + .pi_set_pw_limit = pd692x0_pi_set_pw_limit, +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -210,16 +210,25 @@ out: + static int pse_pi_is_enabled(struct regulator_dev *rdev) + { + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); ++ struct pse_admin_state admin_state = {0}; + const struct pse_controller_ops *ops; + int id, ret; + + ops = pcdev->ops; +- if (!ops->pi_is_enabled) ++ if (!ops->pi_get_admin_state) + return -EOPNOTSUPP; + + id = rdev_get_id(rdev); + mutex_lock(&pcdev->lock); +- ret = ops->pi_is_enabled(pcdev, id); ++ ret = ops->pi_get_admin_state(pcdev, id, &admin_state); ++ if (ret) ++ goto out; ++ ++ if (admin_state.podl_admin_state == ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED || ++ admin_state.c33_admin_state == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED) ++ ret = 1; ++ ++out: + mutex_unlock(&pcdev->lock); + + return ret; +--- a/drivers/net/pse-pd/pse_regulator.c ++++ b/drivers/net/pse-pd/pse_regulator.c +@@ -52,14 +52,6 @@ pse_reg_pi_disable(struct pse_controller + } + + static int +-pse_reg_pi_is_enabled(struct pse_controller_dev *pcdev, int id) +-{ +- struct pse_reg_priv *priv = to_pse_reg(pcdev); +- +- return regulator_is_enabled(priv->ps); +-} +- +-static int + pse_reg_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, + struct pse_admin_state *admin_state) + { +@@ -95,7 +87,6 @@ static const struct pse_controller_ops p + .pi_get_admin_state = pse_reg_pi_get_admin_state, + .pi_get_pw_status = pse_reg_pi_get_pw_status, + .pi_enable = pse_reg_pi_enable, +- .pi_is_enabled = pse_reg_pi_is_enabled, + .pi_disable = pse_reg_pi_disable, + }; + +--- a/drivers/net/pse-pd/tps23881.c ++++ b/drivers/net/pse-pd/tps23881.c +@@ -174,33 +174,6 @@ static int tps23881_pi_disable(struct ps + return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val); + } + +-static int tps23881_pi_is_enabled(struct pse_controller_dev *pcdev, int id) +-{ +- struct tps23881_priv *priv = to_tps23881_priv(pcdev); +- struct i2c_client *client = priv->client; +- bool enabled; +- u8 chan; +- u16 val; +- int ret; +- +- ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS); +- if (ret < 0) +- return ret; +- +- chan = priv->port[id].chan[0]; +- val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4)); +- enabled = !!(val); +- +- if (priv->port[id].is_4p) { +- chan = priv->port[id].chan[1]; +- val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4)); +- enabled &= !!(val); +- } +- +- /* Return enabled status only if both channel are on this state */ +- return enabled; +-} +- + static int + tps23881_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, + struct pse_admin_state *admin_state) +@@ -690,7 +663,6 @@ static const struct pse_controller_ops t + .setup_pi_matrix = tps23881_setup_pi_matrix, + .pi_enable = tps23881_pi_enable, + .pi_disable = tps23881_pi_disable, +- .pi_is_enabled = tps23881_pi_is_enabled, + .pi_get_admin_state = tps23881_pi_get_admin_state, + .pi_get_pw_status = tps23881_pi_get_pw_status, + }; +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -122,8 +122,6 @@ struct ethtool_pse_control_status { + * @pi_get_ext_state: Get the extended state of the PSE PI. + * @pi_get_pw_class: Get the power class of the PSE PI. + * @pi_get_actual_pw: Get actual power of the PSE PI in mW. +- * @pi_is_enabled: Return 1 if the PSE PI is enabled, 0 if not. +- * May also return negative errno. + * @pi_enable: Configure the PSE PI as enabled. + * @pi_disable: Configure the PSE PI as disabled. + * @pi_get_voltage: Return voltage similarly to get_voltage regulator +@@ -145,7 +143,6 @@ struct pse_controller_ops { + struct pse_ext_state_info *ext_state_info); + int (*pi_get_pw_class)(struct pse_controller_dev *pcdev, int id); + int (*pi_get_actual_pw)(struct pse_controller_dev *pcdev, int id); +- int (*pi_is_enabled)(struct pse_controller_dev *pcdev, int id); + int (*pi_enable)(struct pse_controller_dev *pcdev, int id); + int (*pi_disable)(struct pse_controller_dev *pcdev, int id); + int (*pi_get_voltage)(struct pse_controller_dev *pcdev, int id); diff --git a/target/linux/generic/backport-6.12/626-07-v6.13-net-pse-pd-tps23881-Add-power-limit-and-measurement-features.patch b/target/linux/generic/backport-6.12/626-07-v6.13-net-pse-pd-tps23881-Add-power-limit-and-measurement-features.patch new file mode 100644 index 00000000000..de3cc76b4f6 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-07-v6.13-net-pse-pd-tps23881-Add-power-limit-and-measurement-features.patch @@ -0,0 +1,337 @@ +From 7f076ce3f17334964590c2cce49a02c0851c099a Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Fri, 10 Jan 2025 10:40:29 +0100 +Subject: [PATCH] net: pse-pd: tps23881: Add support for power limit and + measurement features + +Expand PSE callbacks to support the newly introduced +pi_get/set_pw_limit() and pi_get_voltage() functions. These callbacks +allow for power limit configuration in the TPS23881 controller. + +Additionally, the patch includes the pi_get_pw_class() the +pi_get_actual_pw(), and the pi_get_pw_limit_ranges') callbacks providing +more comprehensive PoE status reporting. + +Acked-by: Oleksij Rempel +Signed-off-by: Kory Maincent +Signed-off-by: Paolo Abeni +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/tps23881.c | 258 +++++++++++++++++++++++++++++++++- + 1 file changed, 256 insertions(+), 2 deletions(-) + +--- a/drivers/net/pse-pd/tps23881.c ++++ b/drivers/net/pse-pd/tps23881.c +@@ -25,20 +25,32 @@ + #define TPS23881_REG_GEN_MASK 0x17 + #define TPS23881_REG_NBITACC BIT(5) + #define TPS23881_REG_PW_EN 0x19 ++#define TPS23881_REG_2PAIR_POL1 0x1e + #define TPS23881_REG_PORT_MAP 0x26 + #define TPS23881_REG_PORT_POWER 0x29 +-#define TPS23881_REG_POEPLUS 0x40 ++#define TPS23881_REG_4PAIR_POL1 0x2a ++#define TPS23881_REG_INPUT_V 0x2e ++#define TPS23881_REG_CHAN1_A 0x30 ++#define TPS23881_REG_CHAN1_V 0x32 ++#define TPS23881_REG_FOLDBACK 0x40 + #define TPS23881_REG_TPON BIT(0) + #define TPS23881_REG_FWREV 0x41 + #define TPS23881_REG_DEVID 0x43 + #define TPS23881_REG_DEVID_MASK 0xF0 + #define TPS23881_DEVICE_ID 0x02 ++#define TPS23881_REG_CHAN1_CLASS 0x4c + #define TPS23881_REG_SRAM_CTRL 0x60 + #define TPS23881_REG_SRAM_DATA 0x61 + ++#define TPS23881_UV_STEP 3662 ++#define TPS23881_NA_STEP 70190 ++#define TPS23881_MW_STEP 500 ++#define TPS23881_MIN_PI_PW_LIMIT_MW 2000 ++ + struct tps23881_port_desc { + u8 chan[2]; + bool is_4p; ++ int pw_pol; + }; + + struct tps23881_priv { +@@ -102,6 +114,54 @@ static u16 tps23881_set_val(u16 reg_val, + return reg_val; + } + ++static int ++tps23881_pi_set_pw_pol_limit(struct tps23881_priv *priv, int id, u8 pw_pol, ++ bool is_4p) ++{ ++ struct i2c_client *client = priv->client; ++ int ret, reg; ++ u16 val; ++ u8 chan; ++ ++ chan = priv->port[id].chan[0]; ++ if (!is_4p) { ++ reg = TPS23881_REG_2PAIR_POL1 + (chan % 4); ++ } else { ++ /* One chan is enough to configure the 4p PI power limit */ ++ if ((chan % 4) < 2) ++ reg = TPS23881_REG_4PAIR_POL1; ++ else ++ reg = TPS23881_REG_4PAIR_POL1 + 1; ++ } ++ ++ ret = i2c_smbus_read_word_data(client, reg); ++ if (ret < 0) ++ return ret; ++ ++ val = tps23881_set_val(ret, chan, 0, 0xff, pw_pol); ++ return i2c_smbus_write_word_data(client, reg, val); ++} ++ ++static int tps23881_pi_enable_manual_pol(struct tps23881_priv *priv, int id) ++{ ++ struct i2c_client *client = priv->client; ++ int ret; ++ u8 chan; ++ u16 val; ++ ++ ret = i2c_smbus_read_byte_data(client, TPS23881_REG_FOLDBACK); ++ if (ret < 0) ++ return ret; ++ ++ /* No need to test if the chan is PoE4 as setting either bit for a ++ * 4P configured port disables the automatic configuration on both ++ * channels. ++ */ ++ chan = priv->port[id].chan[0]; ++ val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4)); ++ return i2c_smbus_write_byte_data(client, TPS23881_REG_FOLDBACK, val); ++} ++ + static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id) + { + struct tps23881_priv *priv = to_tps23881_priv(pcdev); +@@ -171,7 +231,21 @@ static int tps23881_pi_disable(struct ps + BIT(chan % 4)); + } + +- return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val); ++ ret = i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val); ++ if (ret) ++ return ret; ++ ++ /* No power policy */ ++ if (priv->port[id].pw_pol < 0) ++ return 0; ++ ++ ret = tps23881_pi_enable_manual_pol(priv, id); ++ if (ret < 0) ++ return ret; ++ ++ /* Set power policy */ ++ return tps23881_pi_set_pw_pol_limit(priv, id, priv->port[id].pw_pol, ++ priv->port[id].is_4p); + } + + static int +@@ -246,6 +320,177 @@ tps23881_pi_get_pw_status(struct pse_con + return 0; + } + ++static int tps23881_pi_get_voltage(struct pse_controller_dev *pcdev, int id) ++{ ++ struct tps23881_priv *priv = to_tps23881_priv(pcdev); ++ struct i2c_client *client = priv->client; ++ int ret; ++ u64 uV; ++ ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_INPUT_V); ++ if (ret < 0) ++ return ret; ++ ++ uV = ret & 0x3fff; ++ uV *= TPS23881_UV_STEP; ++ ++ return (int)uV; ++} ++ ++static int ++tps23881_pi_get_chan_current(struct tps23881_priv *priv, u8 chan) ++{ ++ struct i2c_client *client = priv->client; ++ int reg, ret; ++ u64 tmp_64; ++ ++ /* Registers 0x30 to 0x3d */ ++ reg = TPS23881_REG_CHAN1_A + (chan % 4) * 4 + (chan >= 4); ++ ret = i2c_smbus_read_word_data(client, reg); ++ if (ret < 0) ++ return ret; ++ ++ tmp_64 = ret & 0x3fff; ++ tmp_64 *= TPS23881_NA_STEP; ++ /* uA = nA / 1000 */ ++ tmp_64 = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000); ++ return (int)tmp_64; ++} ++ ++static int tps23881_pi_get_pw_class(struct pse_controller_dev *pcdev, ++ int id) ++{ ++ struct tps23881_priv *priv = to_tps23881_priv(pcdev); ++ struct i2c_client *client = priv->client; ++ int ret, reg; ++ u8 chan; ++ ++ chan = priv->port[id].chan[0]; ++ reg = TPS23881_REG_CHAN1_CLASS + (chan % 4); ++ ret = i2c_smbus_read_word_data(client, reg); ++ if (ret < 0) ++ return ret; ++ ++ return tps23881_calc_val(ret, chan, 4, 0x0f); ++} ++ ++static int ++tps23881_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id) ++{ ++ struct tps23881_priv *priv = to_tps23881_priv(pcdev); ++ int ret, uV, uA; ++ u64 tmp_64; ++ u8 chan; ++ ++ ret = tps23881_pi_get_voltage(&priv->pcdev, id); ++ if (ret < 0) ++ return ret; ++ uV = ret; ++ ++ chan = priv->port[id].chan[0]; ++ ret = tps23881_pi_get_chan_current(priv, chan); ++ if (ret < 0) ++ return ret; ++ uA = ret; ++ ++ if (priv->port[id].is_4p) { ++ chan = priv->port[id].chan[1]; ++ ret = tps23881_pi_get_chan_current(priv, chan); ++ if (ret < 0) ++ return ret; ++ uA += ret; ++ } ++ ++ tmp_64 = uV; ++ tmp_64 *= uA; ++ /* mW = uV * uA / 1000000000 */ ++ return DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000); ++} ++ ++static int ++tps23881_pi_get_pw_limit_chan(struct tps23881_priv *priv, u8 chan) ++{ ++ struct i2c_client *client = priv->client; ++ int ret, reg; ++ u16 val; ++ ++ reg = TPS23881_REG_2PAIR_POL1 + (chan % 4); ++ ret = i2c_smbus_read_word_data(client, reg); ++ if (ret < 0) ++ return ret; ++ ++ val = tps23881_calc_val(ret, chan, 0, 0xff); ++ return val * TPS23881_MW_STEP; ++} ++ ++static int tps23881_pi_get_pw_limit(struct pse_controller_dev *pcdev, int id) ++{ ++ struct tps23881_priv *priv = to_tps23881_priv(pcdev); ++ int ret, mW; ++ u8 chan; ++ ++ chan = priv->port[id].chan[0]; ++ ret = tps23881_pi_get_pw_limit_chan(priv, chan); ++ if (ret < 0) ++ return ret; ++ ++ mW = ret; ++ if (priv->port[id].is_4p) { ++ chan = priv->port[id].chan[1]; ++ ret = tps23881_pi_get_pw_limit_chan(priv, chan); ++ if (ret < 0) ++ return ret; ++ mW += ret; ++ } ++ ++ return mW; ++} ++ ++static int tps23881_pi_set_pw_limit(struct pse_controller_dev *pcdev, ++ int id, int max_mW) ++{ ++ struct tps23881_priv *priv = to_tps23881_priv(pcdev); ++ u8 pw_pol; ++ int ret; ++ ++ if (max_mW < TPS23881_MIN_PI_PW_LIMIT_MW || MAX_PI_PW < max_mW) { ++ dev_err(&priv->client->dev, ++ "power limit %d out of ranges [%d,%d]", ++ max_mW, TPS23881_MIN_PI_PW_LIMIT_MW, MAX_PI_PW); ++ return -ERANGE; ++ } ++ ++ ret = tps23881_pi_enable_manual_pol(priv, id); ++ if (ret < 0) ++ return ret; ++ ++ pw_pol = DIV_ROUND_CLOSEST_ULL(max_mW, TPS23881_MW_STEP); ++ ++ /* Save power policy to reconfigure it after a disabled call */ ++ priv->port[id].pw_pol = pw_pol; ++ return tps23881_pi_set_pw_pol_limit(priv, id, pw_pol, ++ priv->port[id].is_4p); ++} ++ ++static int ++tps23881_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id, ++ struct pse_pw_limit_ranges *pw_limit_ranges) ++{ ++ struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; ++ ++ c33_pw_limit_ranges = kzalloc(sizeof(*c33_pw_limit_ranges), ++ GFP_KERNEL); ++ if (!c33_pw_limit_ranges) ++ return -ENOMEM; ++ ++ c33_pw_limit_ranges->min = TPS23881_MIN_PI_PW_LIMIT_MW; ++ c33_pw_limit_ranges->max = MAX_PI_PW; ++ pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges; ++ ++ /* Return the number of ranges */ ++ return 1; ++} ++ + /* Parse managers subnode into a array of device node */ + static int + tps23881_get_of_channels(struct tps23881_priv *priv, +@@ -540,6 +785,9 @@ tps23881_write_port_matrix(struct tps238 + if (port_matrix[i].exist) + priv->port[pi_id].chan[0] = lgcl_chan; + ++ /* Initialize power policy internal value */ ++ priv->port[pi_id].pw_pol = -1; ++ + /* Set hardware port matrix for all ports */ + val |= hw_chan << (lgcl_chan * 2); + +@@ -665,6 +913,12 @@ static const struct pse_controller_ops t + .pi_disable = tps23881_pi_disable, + .pi_get_admin_state = tps23881_pi_get_admin_state, + .pi_get_pw_status = tps23881_pi_get_pw_status, ++ .pi_get_pw_class = tps23881_pi_get_pw_class, ++ .pi_get_actual_pw = tps23881_pi_get_actual_pw, ++ .pi_get_voltage = tps23881_pi_get_voltage, ++ .pi_get_pw_limit = tps23881_pi_get_pw_limit, ++ .pi_set_pw_limit = tps23881_pi_set_pw_limit, ++ .pi_get_pw_limit_ranges = tps23881_pi_get_pw_limit_ranges, + }; + + static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin"; diff --git a/target/linux/generic/backport-6.12/626-08-v6.13-net-pse-pd-Fix-missing-PI-of_node-description.patch b/target/linux/generic/backport-6.12/626-08-v6.13-net-pse-pd-Fix-missing-PI-of_node-description.patch new file mode 100644 index 00000000000..3aa46cde42c --- /dev/null +++ b/target/linux/generic/backport-6.12/626-08-v6.13-net-pse-pd-Fix-missing-PI-of_node-description.patch @@ -0,0 +1,28 @@ +From 10276f3e1c7e7f5de9f0bba58f8a849cb195253d Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Fri, 10 Jan 2025 10:40:30 +0100 +Subject: [PATCH] net: pse-pd: Fix missing PI of_node description + +The PI of_node was not assigned in the regulator_config structure, leading +to failures in resolving the correct supply when different power supplies +are assigned to multiple PIs of a PSE controller. This fix ensures that the +of_node is properly set in the regulator_config, allowing accurate supply +resolution for each PI. + +Acked-by: Oleksij Rempel +Signed-off-by: Kory Maincent +Signed-off-by: Paolo Abeni +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pse_core.c | 1 + + 1 file changed, 1 insertion(+) +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -422,6 +422,7 @@ devm_pse_pi_regulator_register(struct ps + rconfig.dev = pcdev->dev; + rconfig.driver_data = pcdev; + rconfig.init_data = rinit_data; ++ rconfig.of_node = pcdev->pi[id].np; + + rdev = devm_regulator_register(pcdev->dev, rdesc, &rconfig); + if (IS_ERR(rdev)) { diff --git a/target/linux/generic/backport-6.12/626-09-v6.13-net-pse-pd-Clean-ethtool-header-of-PSE-structures.patch b/target/linux/generic/backport-6.12/626-09-v6.13-net-pse-pd-Clean-ethtool-header-of-PSE-structures.patch new file mode 100644 index 00000000000..7d338125a03 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-09-v6.13-net-pse-pd-Clean-ethtool-header-of-PSE-structures.patch @@ -0,0 +1,92 @@ +From 5385f1e1923ca8131eb143567d509b101a344e06 Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Fri, 10 Jan 2025 10:40:31 +0100 +Subject: [PATCH] net: pse-pd: Clean ethtool header of PSE structures + +Remove PSE-specific structures from the ethtool header to improve code +modularity, maintain independent headers, and reduce incremental build +time. + +Signed-off-by: Kory Maincent +Signed-off-by: Paolo Abeni +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pse_core.c | 1 + + include/linux/ethtool.h | 20 -------------------- + include/linux/pse-pd/pse.h | 22 +++++++++++++++++++++- + 3 files changed, 22 insertions(+), 21 deletions(-) +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -6,6 +6,7 @@ + // + + #include ++#include + #include + #include + #include +--- a/include/linux/ethtool.h ++++ b/include/linux/ethtool.h +@@ -1322,24 +1322,4 @@ struct ethtool_forced_speed_map { + + void + ethtool_forced_speed_maps_init(struct ethtool_forced_speed_map *maps, u32 size); +- +-/* C33 PSE extended state and substate. */ +-struct ethtool_c33_pse_ext_state_info { +- enum ethtool_c33_pse_ext_state c33_pse_ext_state; +- union { +- enum ethtool_c33_pse_ext_substate_error_condition error_condition; +- enum ethtool_c33_pse_ext_substate_mr_pse_enable mr_pse_enable; +- enum ethtool_c33_pse_ext_substate_option_detect_ted option_detect_ted; +- enum ethtool_c33_pse_ext_substate_option_vport_lim option_vport_lim; +- enum ethtool_c33_pse_ext_substate_ovld_detected ovld_detected; +- enum ethtool_c33_pse_ext_substate_power_not_available power_not_available; +- enum ethtool_c33_pse_ext_substate_short_detected short_detected; +- u32 __c33_pse_ext_substate; +- }; +-}; +- +-struct ethtool_c33_pse_pw_limit_range { +- u32 min; +- u32 max; +-}; + #endif /* _LINUX_ETHTOOL_H */ +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -5,7 +5,6 @@ + #ifndef _LINUX_PSE_CONTROLLER_H + #define _LINUX_PSE_CONTROLLER_H + +-#include + #include + #include + +@@ -16,6 +15,27 @@ + + struct phy_device; + struct pse_controller_dev; ++struct netlink_ext_ack; ++ ++/* C33 PSE extended state and substate. */ ++struct ethtool_c33_pse_ext_state_info { ++ enum ethtool_c33_pse_ext_state c33_pse_ext_state; ++ union { ++ enum ethtool_c33_pse_ext_substate_error_condition error_condition; ++ enum ethtool_c33_pse_ext_substate_mr_pse_enable mr_pse_enable; ++ enum ethtool_c33_pse_ext_substate_option_detect_ted option_detect_ted; ++ enum ethtool_c33_pse_ext_substate_option_vport_lim option_vport_lim; ++ enum ethtool_c33_pse_ext_substate_ovld_detected ovld_detected; ++ enum ethtool_c33_pse_ext_substate_power_not_available power_not_available; ++ enum ethtool_c33_pse_ext_substate_short_detected short_detected; ++ u32 __c33_pse_ext_substate; ++ }; ++}; ++ ++struct ethtool_c33_pse_pw_limit_range { ++ u32 min; ++ u32 max; ++}; + + /** + * struct pse_control_config - PSE control/channel configuration. diff --git a/target/linux/generic/backport-6.12/626-10-v6.17-net-pse-pd-Introduce-attached_phydev-to-pse-control.patch b/target/linux/generic/backport-6.12/626-10-v6.17-net-pse-pd-Introduce-attached_phydev-to-pse-control.patch new file mode 100644 index 00000000000..b00c0e14759 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-10-v6.17-net-pse-pd-Introduce-attached_phydev-to-pse-control.patch @@ -0,0 +1,171 @@ +From fa2f0454174c2f33005f5a6e6f70c7160a15b2a1 Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:00 +0200 +Subject: [PATCH] net: pse-pd: Introduce attached_phydev to pse control + +In preparation for reporting PSE events via ethtool notifications, +introduce an attached_phydev field in the pse_control structure. +This field stores the phy_device associated with the PSE PI, +ensuring that notifications are sent to the correct network +interface. + +The attached_phydev pointer is directly tied to the PHY lifecycle. It +is set when the PHY is registered and cleared when the PHY is removed. +There is no need to use a refcount, as doing so could interfere with +the PHY removal process. + +Signed-off-by: Kory Maincent (Dent Project) +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-1-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/mdio/fwnode_mdio.c | 26 ++++++++++++++------------ + drivers/net/pse-pd/pse_core.c | 11 ++++++++--- + include/linux/pse-pd/pse.h | 6 ++++-- + 3 files changed, 26 insertions(+), 17 deletions(-) +--- a/drivers/net/mdio/fwnode_mdio.c ++++ b/drivers/net/mdio/fwnode_mdio.c +@@ -18,7 +18,8 @@ MODULE_LICENSE("GPL"); + MODULE_DESCRIPTION("FWNODE MDIO bus (Ethernet PHY) accessors"); + + static struct pse_control * +-fwnode_find_pse_control(struct fwnode_handle *fwnode) ++fwnode_find_pse_control(struct fwnode_handle *fwnode, ++ struct phy_device *phydev) + { + struct pse_control *psec; + struct device_node *np; +@@ -30,7 +31,7 @@ fwnode_find_pse_control(struct fwnode_ha + if (!np) + return NULL; + +- psec = of_pse_control_get(np); ++ psec = of_pse_control_get(np, phydev); + if (PTR_ERR(psec) == -ENOENT) + return NULL; + +@@ -128,15 +129,9 @@ int fwnode_mdiobus_register_phy(struct m + u32 phy_id; + int rc; + +- psec = fwnode_find_pse_control(child); +- if (IS_ERR(psec)) +- return PTR_ERR(psec); +- + mii_ts = fwnode_find_mii_timestamper(child); +- if (IS_ERR(mii_ts)) { +- rc = PTR_ERR(mii_ts); +- goto clean_pse; +- } ++ if (IS_ERR(mii_ts)) ++ return PTR_ERR(mii_ts); + + is_c45 = fwnode_device_is_compatible(child, "ethernet-phy-ieee802.3-c45"); + if (is_c45 || fwnode_get_phy_id(child, &phy_id)) +@@ -169,6 +164,12 @@ int fwnode_mdiobus_register_phy(struct m + goto clean_phy; + } + ++ psec = fwnode_find_pse_control(child, phy); ++ if (IS_ERR(psec)) { ++ rc = PTR_ERR(psec); ++ goto unregister_phy; ++ } ++ + phy->psec = psec; + + /* phy->mii_ts may already be defined by the PHY driver. A +@@ -180,12 +181,13 @@ int fwnode_mdiobus_register_phy(struct m + + return 0; + ++unregister_phy: ++ if (is_acpi_node(child) || is_of_node(child)) ++ phy_device_remove(phy); + clean_phy: + phy_device_free(phy); + clean_mii_ts: + unregister_mii_timestamper(mii_ts); +-clean_pse: +- pse_control_put(psec); + + return rc; + } +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -23,6 +23,7 @@ static LIST_HEAD(pse_controller_list); + * @list: list entry for the pcdev's PSE controller list + * @id: ID of the PSE line in the PSE controller device + * @refcnt: Number of gets of this pse_control ++ * @attached_phydev: PHY device pointer attached by the PSE control + */ + struct pse_control { + struct pse_controller_dev *pcdev; +@@ -30,6 +31,7 @@ struct pse_control { + struct list_head list; + unsigned int id; + struct kref refcnt; ++ struct phy_device *attached_phydev; + }; + + static int of_load_single_pse_pi_pairset(struct device_node *node, +@@ -599,7 +601,8 @@ void pse_control_put(struct pse_control + EXPORT_SYMBOL_GPL(pse_control_put); + + static struct pse_control * +-pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index) ++pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index, ++ struct phy_device *phydev) + { + struct pse_control *psec; + int ret; +@@ -638,6 +641,7 @@ pse_control_get_internal(struct pse_cont + psec->pcdev = pcdev; + list_add(&psec->list, &pcdev->pse_control_head); + psec->id = index; ++ psec->attached_phydev = phydev; + kref_init(&psec->refcnt); + + return psec; +@@ -693,7 +697,8 @@ static int psec_id_xlate(struct pse_cont + return pse_spec->args[0]; + } + +-struct pse_control *of_pse_control_get(struct device_node *node) ++struct pse_control *of_pse_control_get(struct device_node *node, ++ struct phy_device *phydev) + { + struct pse_controller_dev *r, *pcdev; + struct of_phandle_args args; +@@ -743,7 +748,7 @@ struct pse_control *of_pse_control_get(s + } + + /* pse_list_mutex also protects the pcdev's pse_control list */ +- psec = pse_control_get_internal(pcdev, psec_id); ++ psec = pse_control_get_internal(pcdev, psec_id, phydev); + + out: + mutex_unlock(&pse_list_mutex); +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -250,7 +250,8 @@ struct device; + int devm_pse_controller_register(struct device *dev, + struct pse_controller_dev *pcdev); + +-struct pse_control *of_pse_control_get(struct device_node *node); ++struct pse_control *of_pse_control_get(struct device_node *node, ++ struct phy_device *phydev); + void pse_control_put(struct pse_control *psec); + + int pse_ethtool_get_status(struct pse_control *psec, +@@ -270,7 +271,8 @@ bool pse_has_c33(struct pse_control *pse + + #else + +-static inline struct pse_control *of_pse_control_get(struct device_node *node) ++static inline struct pse_control *of_pse_control_get(struct device_node *node, ++ struct phy_device *phydev) + { + return ERR_PTR(-ENOENT); + } diff --git a/target/linux/generic/backport-6.12/626-11-v6.17-net-pse-pd-Add-support-for-reporting-events.patch b/target/linux/generic/backport-6.12/626-11-v6.17-net-pse-pd-Add-support-for-reporting-events.patch new file mode 100644 index 00000000000..ec99fcab821 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-11-v6.17-net-pse-pd-Add-support-for-reporting-events.patch @@ -0,0 +1,363 @@ +# ADAPTED FOR OPENWRT 6.12.67 - Documentation changes removed +# Original commit: fc0e6db30941 +From fc0e6db30941a66e284b8516b82356f97f31061d Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:01 +0200 +Subject: [PATCH] net: pse-pd: Add support for reporting events + +Add support for devm_pse_irq_helper() to register PSE interrupts and report +events such as over-current or over-temperature conditions. This follows a +similar approach to the regulator API but also sends notifications using a +dedicated PSE ethtool netlink socket. + +Signed-off-by: Kory Maincent (Dent Project) +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-2-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + Documentation/netlink/specs/ethtool.yaml | 34 ++++ + Documentation/networking/ethtool-netlink.rst | 19 ++ + drivers/net/pse-pd/pse_core.c | 179 ++++++++++++++++++ + include/linux/ethtool_netlink.h | 7 + + include/linux/pse-pd/pse.h | 20 ++ + .../uapi/linux/ethtool_netlink_generated.h | 19 ++ + net/ethtool/pse-pd.c | 39 ++++ + 7 files changed, 317 insertions(+) + +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -7,10 +7,14 @@ + + #include + #include ++#include + #include ++#include + #include + #include + #include ++#include ++#include + + static DEFINE_MUTEX(pse_list_mutex); + static LIST_HEAD(pse_controller_list); +@@ -210,6 +214,48 @@ out: + return ret; + } + ++/** ++ * pse_control_find_net_by_id - Find net attached to the pse control id ++ * @pcdev: a pointer to the PSE ++ * @id: index of the PSE control ++ * ++ * Return: pse_control pointer or NULL. The device returned has had a ++ * reference added and the pointer is safe until the user calls ++ * pse_control_put() to indicate they have finished with it. ++ */ ++static struct pse_control * ++pse_control_find_by_id(struct pse_controller_dev *pcdev, int id) ++{ ++ struct pse_control *psec; ++ ++ mutex_lock(&pse_list_mutex); ++ list_for_each_entry(psec, &pcdev->pse_control_head, list) { ++ if (psec->id == id) { ++ kref_get(&psec->refcnt); ++ mutex_unlock(&pse_list_mutex); ++ return psec; ++ } ++ } ++ mutex_unlock(&pse_list_mutex); ++ return NULL; ++} ++ ++/** ++ * pse_control_get_netdev - Return netdev associated to a PSE control ++ * @psec: PSE control pointer ++ * ++ * Return: netdev pointer or NULL ++ */ ++static struct net_device *pse_control_get_netdev(struct pse_control *psec) ++{ ++ ASSERT_RTNL(); ++ ++ if (!psec || !psec->attached_phydev) ++ return NULL; ++ ++ return psec->attached_phydev->attached_dev; ++} ++ + static int pse_pi_is_enabled(struct regulator_dev *rdev) + { + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); +@@ -559,6 +605,139 @@ int devm_pse_controller_register(struct + } + EXPORT_SYMBOL_GPL(devm_pse_controller_register); + ++struct pse_irq { ++ struct pse_controller_dev *pcdev; ++ struct pse_irq_desc desc; ++ unsigned long *notifs; ++}; ++ ++/** ++ * pse_to_regulator_notifs - Convert PSE notifications to Regulator ++ * notifications ++ * @notifs: PSE notifications ++ * ++ * Return: Regulator notifications ++ */ ++static unsigned long pse_to_regulator_notifs(unsigned long notifs) ++{ ++ unsigned long rnotifs = 0; ++ ++ if (notifs & ETHTOOL_PSE_EVENT_OVER_CURRENT) ++ rnotifs |= REGULATOR_EVENT_OVER_CURRENT; ++ if (notifs & ETHTOOL_PSE_EVENT_OVER_TEMP) ++ rnotifs |= REGULATOR_EVENT_OVER_TEMP; ++ ++ return rnotifs; ++} ++ ++/** ++ * pse_isr - IRQ handler for PSE ++ * @irq: irq number ++ * @data: pointer to user interrupt structure ++ * ++ * Return: irqreturn_t - status of IRQ ++ */ ++static irqreturn_t pse_isr(int irq, void *data) ++{ ++ struct pse_controller_dev *pcdev; ++ unsigned long notifs_mask = 0; ++ struct pse_irq_desc *desc; ++ struct pse_irq *h = data; ++ int ret, i; ++ ++ desc = &h->desc; ++ pcdev = h->pcdev; ++ ++ /* Clear notifs mask */ ++ memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs)); ++ mutex_lock(&pcdev->lock); ++ ret = desc->map_event(irq, pcdev, h->notifs, ¬ifs_mask); ++ mutex_unlock(&pcdev->lock); ++ if (ret || !notifs_mask) ++ return IRQ_NONE; ++ ++ for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) { ++ unsigned long notifs, rnotifs; ++ struct net_device *netdev; ++ struct pse_control *psec; ++ ++ /* Do nothing PI not described */ ++ if (!pcdev->pi[i].rdev) ++ continue; ++ ++ notifs = h->notifs[i]; ++ dev_dbg(h->pcdev->dev, ++ "Sending PSE notification EVT 0x%lx\n", notifs); ++ ++ psec = pse_control_find_by_id(pcdev, i); ++ rtnl_lock(); ++ netdev = pse_control_get_netdev(psec); ++ if (netdev) ++ ethnl_pse_send_ntf(netdev, notifs); ++ rtnl_unlock(); ++ pse_control_put(psec); ++ ++ rnotifs = pse_to_regulator_notifs(notifs); ++ regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs, ++ NULL); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * devm_pse_irq_helper - Register IRQ based PSE event notifier ++ * @pcdev: a pointer to the PSE ++ * @irq: the irq value to be passed to request_irq ++ * @irq_flags: the flags to be passed to request_irq ++ * @d: PSE interrupt description ++ * ++ * Return: 0 on success and errno on failure ++ */ ++int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq, ++ int irq_flags, const struct pse_irq_desc *d) ++{ ++ struct device *dev = pcdev->dev; ++ size_t irq_name_len; ++ struct pse_irq *h; ++ char *irq_name; ++ int ret; ++ ++ if (!d || !d->map_event || !d->name) ++ return -EINVAL; ++ ++ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); ++ if (!h) ++ return -ENOMEM; ++ ++ h->pcdev = pcdev; ++ h->desc = *d; ++ ++ /* IRQ name len is pcdev dev name + 5 char + irq desc name + 1 */ ++ irq_name_len = strlen(dev_name(pcdev->dev)) + 5 + strlen(d->name) + 1; ++ irq_name = devm_kzalloc(dev, irq_name_len, GFP_KERNEL); ++ if (!irq_name) ++ return -ENOMEM; ++ ++ snprintf(irq_name, irq_name_len, "pse-%s:%s", dev_name(pcdev->dev), ++ d->name); ++ ++ h->notifs = devm_kcalloc(dev, pcdev->nr_lines, ++ sizeof(*h->notifs), GFP_KERNEL); ++ if (!h->notifs) ++ return -ENOMEM; ++ ++ ret = devm_request_threaded_irq(dev, irq, NULL, pse_isr, ++ IRQF_ONESHOT | irq_flags, ++ irq_name, h); ++ if (ret) ++ dev_err(pcdev->dev, "Failed to request IRQ %d\n", irq); ++ ++ pcdev->irq = irq; ++ return ret; ++} ++EXPORT_SYMBOL_GPL(devm_pse_irq_helper); ++ + /* PSE control section */ + + static void __pse_control_release(struct kref *kref) +--- a/include/linux/ethtool_netlink.h ++++ b/include/linux/ethtool_netlink.h +@@ -43,6 +43,8 @@ void ethtool_aggregate_rmon_stats(struct + struct ethtool_rmon_stats *rmon_stats); + bool ethtool_dev_mm_supported(struct net_device *dev); + ++void ethnl_pse_send_ntf(struct net_device *netdev, unsigned long notif); ++ + #else + static inline int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd) + { +@@ -120,6 +122,11 @@ static inline bool ethtool_dev_mm_suppor + return false; + } + ++static inline void ethnl_pse_send_ntf(struct phy_device *phydev, ++ unsigned long notif) ++{ ++} ++ + #endif /* IS_ENABLED(CONFIG_ETHTOOL_NETLINK) */ + + static inline int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -7,12 +7,15 @@ + + #include + #include ++#include ++#include + + /* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */ + #define MAX_PI_CURRENT 1920000 + /* Maximum power in mW according to IEEE 802.3-2022 Table 145-16 */ + #define MAX_PI_PW 99900 + ++struct net_device; + struct phy_device; + struct pse_controller_dev; + struct netlink_ext_ack; +@@ -38,6 +41,19 @@ struct ethtool_c33_pse_pw_limit_range { + }; + + /** ++ * struct pse_irq_desc - notification sender description for IRQ based events. ++ * ++ * @name: the visible name for the IRQ ++ * @map_event: driver callback to map IRQ status into PSE devices with events. ++ */ ++struct pse_irq_desc { ++ const char *name; ++ int (*map_event)(int irq, struct pse_controller_dev *pcdev, ++ unsigned long *notifs, ++ unsigned long *notifs_mask); ++}; ++ ++/** + * struct pse_control_config - PSE control/channel configuration. + * + * @podl_admin_control: set PoDL PSE admin control as described in +@@ -228,6 +244,7 @@ struct pse_pi { + * @types: types of the PSE controller + * @pi: table of PSE PIs described in this controller device + * @no_of_pse_pi: flag set if the pse_pis devicetree node is not used ++ * @irq: PSE interrupt + */ + struct pse_controller_dev { + const struct pse_controller_ops *ops; +@@ -241,6 +258,7 @@ struct pse_controller_dev { + enum ethtool_pse_types types; + struct pse_pi *pi; + bool no_of_pse_pi; ++ int irq; + }; + + #if IS_ENABLED(CONFIG_PSE_CONTROLLER) +@@ -249,6 +267,8 @@ void pse_controller_unregister(struct ps + struct device; + int devm_pse_controller_register(struct device *dev, + struct pse_controller_dev *pcdev); ++int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq, ++ int irq_flags, const struct pse_irq_desc *d); + + struct pse_control *of_pse_control_get(struct device_node *node, + struct phy_device *phydev); +--- a/net/ethtool/pse-pd.c ++++ b/net/ethtool/pse-pd.c +@@ -315,3 +315,42 @@ const struct ethnl_request_ops ethnl_pse + .set = ethnl_set_pse, + /* PSE has no notification */ + }; ++ ++void ethnl_pse_send_ntf(struct net_device *netdev, unsigned long notifs) ++{ ++ void *reply_payload; ++ struct sk_buff *skb; ++ int reply_len; ++ int ret; ++ ++ ASSERT_RTNL(); ++ ++ if (!netdev || !notifs) ++ return; ++ ++ reply_len = ethnl_reply_header_size() + ++ nla_total_size(sizeof(u32)); /* _PSE_NTF_EVENTS */ ++ ++ skb = genlmsg_new(reply_len, GFP_KERNEL); ++ if (!skb) ++ return; ++ ++ reply_payload = ethnl_bcastmsg_put(skb, ETHTOOL_MSG_PSE_NTF); ++ if (!reply_payload) ++ goto err_skb; ++ ++ ret = ethnl_fill_reply_header(skb, netdev, ETHTOOL_A_PSE_NTF_HEADER); ++ if (ret < 0) ++ goto err_skb; ++ ++ if (nla_put_uint(skb, ETHTOOL_A_PSE_NTF_EVENTS, notifs)) ++ goto err_skb; ++ ++ genlmsg_end(skb, reply_payload); ++ ethnl_multicast(skb, netdev); ++ return; ++ ++err_skb: ++ nlmsg_free(skb); ++} ++EXPORT_SYMBOL_GPL(ethnl_pse_send_ntf); diff --git a/target/linux/generic/backport-6.12/626-12-v6.17-net-pse-pd-tps23881-Add-support-for-PSE-events.patch b/target/linux/generic/backport-6.12/626-12-v6.17-net-pse-pd-tps23881-Add-support-for-PSE-events.patch new file mode 100644 index 00000000000..8afcb3f8700 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-12-v6.17-net-pse-pd-tps23881-Add-support-for-PSE-events.patch @@ -0,0 +1,252 @@ +From f5e7aecaa4efcd4c85477b6a62f94fea668031db Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:02 +0200 +Subject: [PATCH] net: pse-pd: tps23881: Add support for PSE events and + interrupts + +Add support for PSE event reporting through interrupts. Set up the newly +introduced devm_pse_irq_helper helper to register the interrupt. Events are +reported for over-current and over-temperature conditions. + +Signed-off-by: Kory Maincent (Dent Project) +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-3-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/tps23881.c | 189 +++++++++++++++++++++++++++++++++- + 1 file changed, 187 insertions(+), 2 deletions(-) + +--- a/drivers/net/pse-pd/tps23881.c ++++ b/drivers/net/pse-pd/tps23881.c +@@ -16,7 +16,15 @@ + #include + + #define TPS23881_MAX_CHANS 8 ++#define TPS23881_MAX_IRQ_RETRIES 10 + ++#define TPS23881_REG_IT 0x0 ++#define TPS23881_REG_IT_MASK 0x1 ++#define TPS23881_REG_IT_IFAULT BIT(5) ++#define TPS23881_REG_IT_SUPF BIT(7) ++#define TPS23881_REG_FAULT 0x7 ++#define TPS23881_REG_SUPF_EVENT 0xb ++#define TPS23881_REG_TSD BIT(7) + #define TPS23881_REG_PW_STATUS 0x10 + #define TPS23881_REG_OP_MODE 0x12 + #define TPS23881_OP_MODE_SEMIAUTO 0xaaaa +@@ -24,6 +32,7 @@ + #define TPS23881_REG_DET_CLA_EN 0x14 + #define TPS23881_REG_GEN_MASK 0x17 + #define TPS23881_REG_NBITACC BIT(5) ++#define TPS23881_REG_INTEN BIT(7) + #define TPS23881_REG_PW_EN 0x19 + #define TPS23881_REG_2PAIR_POL1 0x1e + #define TPS23881_REG_PORT_MAP 0x26 +@@ -51,6 +60,7 @@ struct tps23881_port_desc { + u8 chan[2]; + bool is_4p; + int pw_pol; ++ bool exist; + }; + + struct tps23881_priv { +@@ -782,8 +792,10 @@ tps23881_write_port_matrix(struct tps238 + hw_chan = port_matrix[i].hw_chan[0] % 4; + + /* Set software port matrix for existing ports */ +- if (port_matrix[i].exist) ++ if (port_matrix[i].exist) { + priv->port[pi_id].chan[0] = lgcl_chan; ++ priv->port[pi_id].exist = true; ++ } + + /* Initialize power policy internal value */ + priv->port[pi_id].pw_pol = -1; +@@ -1017,6 +1029,173 @@ static int tps23881_flash_sram_fw(struct + return 0; + } + ++/* Convert interrupt events to 0xff to be aligned with the chan ++ * number. ++ */ ++static u8 tps23881_irq_export_chans_helper(u16 reg_val, u8 field_offset) ++{ ++ u8 val; ++ ++ val = (reg_val >> (4 + field_offset) & 0xf0) | ++ (reg_val >> field_offset & 0x0f); ++ ++ return val; ++} ++ ++/* Convert chan number to port number */ ++static void tps23881_set_notifs_helper(struct tps23881_priv *priv, ++ u8 chans, ++ unsigned long *notifs, ++ unsigned long *notifs_mask, ++ enum ethtool_pse_event event) ++{ ++ u8 chan; ++ int i; ++ ++ if (!chans) ++ return; ++ ++ for (i = 0; i < TPS23881_MAX_CHANS; i++) { ++ if (!priv->port[i].exist) ++ continue; ++ /* No need to look at the 2nd channel in case of PoE4 as ++ * both registers are set. ++ */ ++ chan = priv->port[i].chan[0]; ++ ++ if (BIT(chan) & chans) { ++ *notifs_mask |= BIT(i); ++ notifs[i] |= event; ++ } ++ } ++} ++ ++static void tps23881_irq_event_over_temp(struct tps23881_priv *priv, ++ u16 reg_val, ++ unsigned long *notifs, ++ unsigned long *notifs_mask) ++{ ++ int i; ++ ++ if (reg_val & TPS23881_REG_TSD) { ++ for (i = 0; i < TPS23881_MAX_CHANS; i++) { ++ if (!priv->port[i].exist) ++ continue; ++ ++ *notifs_mask |= BIT(i); ++ notifs[i] |= ETHTOOL_PSE_EVENT_OVER_TEMP; ++ } ++ } ++} ++ ++static void tps23881_irq_event_over_current(struct tps23881_priv *priv, ++ u16 reg_val, ++ unsigned long *notifs, ++ unsigned long *notifs_mask) ++{ ++ u8 chans; ++ ++ chans = tps23881_irq_export_chans_helper(reg_val, 0); ++ if (chans) ++ tps23881_set_notifs_helper(priv, chans, notifs, notifs_mask, ++ ETHTOOL_PSE_EVENT_OVER_CURRENT); ++} ++ ++static int tps23881_irq_event_handler(struct tps23881_priv *priv, u16 reg, ++ unsigned long *notifs, ++ unsigned long *notifs_mask) ++{ ++ struct i2c_client *client = priv->client; ++ int ret; ++ ++ /* The Supply event bit is repeated twice so we only need to read ++ * the one from the first byte. ++ */ ++ if (reg & TPS23881_REG_IT_SUPF) { ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_SUPF_EVENT); ++ if (ret < 0) ++ return ret; ++ tps23881_irq_event_over_temp(priv, ret, notifs, notifs_mask); ++ } ++ ++ if (reg & (TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_IFAULT << 8)) { ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_FAULT); ++ if (ret < 0) ++ return ret; ++ tps23881_irq_event_over_current(priv, ret, notifs, notifs_mask); ++ } ++ ++ return 0; ++} ++ ++static int tps23881_irq_handler(int irq, struct pse_controller_dev *pcdev, ++ unsigned long *notifs, ++ unsigned long *notifs_mask) ++{ ++ struct tps23881_priv *priv = to_tps23881_priv(pcdev); ++ struct i2c_client *client = priv->client; ++ int ret, it_mask, retry; ++ ++ /* Get interruption mask */ ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_IT_MASK); ++ if (ret < 0) ++ return ret; ++ it_mask = ret; ++ ++ /* Read interrupt register until it frees the interruption pin. */ ++ retry = 0; ++ while (true) { ++ if (retry > TPS23881_MAX_IRQ_RETRIES) { ++ dev_err(&client->dev, "interrupt never freed"); ++ return -ETIMEDOUT; ++ } ++ ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_IT); ++ if (ret < 0) ++ return ret; ++ ++ /* No more relevant interruption */ ++ if (!(ret & it_mask)) ++ return 0; ++ ++ ret = tps23881_irq_event_handler(priv, (u16)ret, notifs, ++ notifs_mask); ++ if (ret) ++ return ret; ++ ++ retry++; ++ } ++ return 0; ++} ++ ++static int tps23881_setup_irq(struct tps23881_priv *priv, int irq) ++{ ++ struct i2c_client *client = priv->client; ++ struct pse_irq_desc irq_desc = { ++ .name = "tps23881-irq", ++ .map_event = tps23881_irq_handler, ++ }; ++ int ret; ++ u16 val; ++ ++ val = TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_SUPF; ++ val |= val << 8; ++ ret = i2c_smbus_write_word_data(client, TPS23881_REG_IT_MASK, val); ++ if (ret) ++ return ret; ++ ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_GEN_MASK); ++ if (ret < 0) ++ return ret; ++ ++ val = (u16)(ret | TPS23881_REG_INTEN | TPS23881_REG_INTEN << 8); ++ ret = i2c_smbus_write_word_data(client, TPS23881_REG_GEN_MASK, val); ++ if (ret < 0) ++ return ret; ++ ++ return devm_pse_irq_helper(&priv->pcdev, irq, 0, &irq_desc); ++} ++ + static int tps23881_i2c_probe(struct i2c_client *client) + { + struct device *dev = &client->dev; +@@ -1097,6 +1276,12 @@ static int tps23881_i2c_probe(struct i2c + "failed to register PSE controller\n"); + } + ++ if (client->irq) { ++ ret = tps23881_setup_irq(priv, client->irq); ++ if (ret) ++ return ret; ++ } ++ + return ret; + } + diff --git a/target/linux/generic/backport-6.12/626-13-v6.17-net-pse-pd-Add-support-for-PSE-power-domains.patch b/target/linux/generic/backport-6.12/626-13-v6.17-net-pse-pd-Add-support-for-PSE-power-domains.patch new file mode 100644 index 00000000000..a02bbbc28b1 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-13-v6.17-net-pse-pd-Add-support-for-PSE-power-domains.patch @@ -0,0 +1,218 @@ +From 50f8b341d26826aa5fdccb8f497fbff2500934b3 Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:03 +0200 +Subject: [PATCH] net: pse-pd: Add support for PSE power domains + +Introduce PSE power domain support as groundwork for upcoming port +priority features. Multiple PSE PIs can now be grouped under a single +PSE power domain, enabling future enhancements like defining available +power budgets, port priority modes, and disconnection policies. This +setup will allow the system to assess whether activating a port would +exceed the available power budget, preventing over-budget states +proactively. + +Signed-off-by: Kory Maincent (Dent Project) +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-4-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pse_core.c | 140 ++++++++++++++++++++++++++++++++++ + include/linux/pse-pd/pse.h | 2 + + 2 files changed, 142 insertions(+) +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -16,8 +16,12 @@ + #include + #include + ++#define PSE_PW_D_LIMIT INT_MAX ++ + static DEFINE_MUTEX(pse_list_mutex); + static LIST_HEAD(pse_controller_list); ++static DEFINE_XARRAY_ALLOC(pse_pw_d_map); ++static DEFINE_MUTEX(pse_pw_d_mutex); + + /** + * struct pse_control - a PSE control +@@ -38,6 +42,18 @@ struct pse_control { + struct phy_device *attached_phydev; + }; + ++/** ++ * struct pse_power_domain - a PSE power domain ++ * @id: ID of the power domain ++ * @supply: Power supply the Power Domain ++ * @refcnt: Number of gets of this pse_power_domain ++ */ ++struct pse_power_domain { ++ int id; ++ struct regulator *supply; ++ struct kref refcnt; ++}; ++ + static int of_load_single_pse_pi_pairset(struct device_node *node, + struct pse_pi *pi, + int pairset_num) +@@ -485,6 +501,125 @@ devm_pse_pi_regulator_register(struct ps + return 0; + } + ++static void __pse_pw_d_release(struct kref *kref) ++{ ++ struct pse_power_domain *pw_d = container_of(kref, ++ struct pse_power_domain, ++ refcnt); ++ ++ regulator_put(pw_d->supply); ++ xa_erase(&pse_pw_d_map, pw_d->id); ++ mutex_unlock(&pse_pw_d_mutex); ++} ++ ++/** ++ * pse_flush_pw_ds - flush all PSE power domains of a PSE ++ * @pcdev: a pointer to the initialized PSE controller device ++ */ ++static void pse_flush_pw_ds(struct pse_controller_dev *pcdev) ++{ ++ struct pse_power_domain *pw_d; ++ int i; ++ ++ for (i = 0; i < pcdev->nr_lines; i++) { ++ if (!pcdev->pi[i].pw_d) ++ continue; ++ ++ pw_d = xa_load(&pse_pw_d_map, pcdev->pi[i].pw_d->id); ++ if (!pw_d) ++ continue; ++ ++ kref_put_mutex(&pw_d->refcnt, __pse_pw_d_release, ++ &pse_pw_d_mutex); ++ } ++} ++ ++/** ++ * devm_pse_alloc_pw_d - allocate a new PSE power domain for a device ++ * @dev: device that is registering this PSE power domain ++ * ++ * Return: Pointer to the newly allocated PSE power domain or error pointers ++ */ ++static struct pse_power_domain *devm_pse_alloc_pw_d(struct device *dev) ++{ ++ struct pse_power_domain *pw_d; ++ int index, ret; ++ ++ pw_d = devm_kzalloc(dev, sizeof(*pw_d), GFP_KERNEL); ++ if (!pw_d) ++ return ERR_PTR(-ENOMEM); ++ ++ ret = xa_alloc(&pse_pw_d_map, &index, pw_d, XA_LIMIT(1, PSE_PW_D_LIMIT), ++ GFP_KERNEL); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ kref_init(&pw_d->refcnt); ++ pw_d->id = index; ++ return pw_d; ++} ++ ++/** ++ * pse_register_pw_ds - register the PSE power domains for a PSE ++ * @pcdev: a pointer to the PSE controller device ++ * ++ * Return: 0 on success and failure value on error ++ */ ++static int pse_register_pw_ds(struct pse_controller_dev *pcdev) ++{ ++ int i, ret = 0; ++ ++ mutex_lock(&pse_pw_d_mutex); ++ for (i = 0; i < pcdev->nr_lines; i++) { ++ struct regulator_dev *rdev = pcdev->pi[i].rdev; ++ struct pse_power_domain *pw_d; ++ struct regulator *supply; ++ bool present = false; ++ unsigned long index; ++ ++ /* No regulator or regulator parent supply registered. ++ * We need a regulator parent to register a PSE power domain ++ */ ++ if (!rdev || !rdev->supply) ++ continue; ++ ++ xa_for_each(&pse_pw_d_map, index, pw_d) { ++ /* Power supply already registered as a PSE power ++ * domain. ++ */ ++ if (regulator_is_equal(pw_d->supply, rdev->supply)) { ++ present = true; ++ pcdev->pi[i].pw_d = pw_d; ++ break; ++ } ++ } ++ if (present) { ++ kref_get(&pw_d->refcnt); ++ continue; ++ } ++ ++ pw_d = devm_pse_alloc_pw_d(pcdev->dev); ++ if (IS_ERR(pw_d)) { ++ ret = PTR_ERR(pw_d); ++ goto out; ++ } ++ ++ supply = regulator_get(&rdev->dev, rdev->supply_name); ++ if (IS_ERR(supply)) { ++ xa_erase(&pse_pw_d_map, pw_d->id); ++ ret = PTR_ERR(supply); ++ goto out; ++ } ++ ++ pw_d->supply = supply; ++ pcdev->pi[i].pw_d = pw_d; ++ } ++ ++out: ++ mutex_unlock(&pse_pw_d_mutex); ++ return ret; ++} ++ + /** + * pse_controller_register - register a PSE controller device + * @pcdev: a pointer to the initialized PSE controller device +@@ -544,6 +679,10 @@ int pse_controller_register(struct pse_c + return ret; + } + ++ ret = pse_register_pw_ds(pcdev); ++ if (ret) ++ return ret; ++ + mutex_lock(&pse_list_mutex); + list_add(&pcdev->list, &pse_controller_list); + mutex_unlock(&pse_list_mutex); +@@ -558,6 +697,7 @@ EXPORT_SYMBOL_GPL(pse_controller_registe + */ + void pse_controller_unregister(struct pse_controller_dev *pcdev) + { ++ pse_flush_pw_ds(pcdev); + pse_release_pis(pcdev); + mutex_lock(&pse_list_mutex); + list_del(&pcdev->list); +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -222,12 +222,14 @@ struct pse_pi_pairset { + * @np: device node pointer of the PSE PI node + * @rdev: regulator represented by the PSE PI + * @admin_state_enabled: PI enabled state ++ * @pw_d: Power domain of the PSE PI + */ + struct pse_pi { + struct pse_pi_pairset pairset[2]; + struct device_node *np; + struct regulator_dev *rdev; + bool admin_state_enabled; ++ struct pse_power_domain *pw_d; + }; + + /** diff --git a/target/linux/generic/backport-6.12/626-14-v6.17-net-pse-pd-ethtool-Add-support-for-power-domains-index.patch b/target/linux/generic/backport-6.12/626-14-v6.17-net-pse-pd-ethtool-Add-support-for-power-domains-index.patch new file mode 100644 index 00000000000..1124882a6a3 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-14-v6.17-net-pse-pd-ethtool-Add-support-for-power-domains-index.patch @@ -0,0 +1,78 @@ +# ADAPTED FOR OPENWRT 6.12.67 - Documentation changes removed +# Original commit: 1176978ed851 +From 1176978ed851952652ddea3685e2f71a0e5d61ff Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:04 +0200 +Subject: [PATCH] net: ethtool: Add support for new power domains index + description + +Report the index of the newly introduced PSE power domain to the user, +enabling improved management of the power budget for PSE devices. + +Signed-off-by: Kory Maincent (Dent Project) +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-5-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + Documentation/netlink/specs/ethtool.yaml | 5 +++++ + Documentation/networking/ethtool-netlink.rst | 4 ++++ + drivers/net/pse-pd/pse_core.c | 3 +++ + include/linux/pse-pd/pse.h | 2 ++ + include/uapi/linux/ethtool_netlink_generated.h | 1 + + net/ethtool/pse-pd.c | 7 +++++++ + 6 files changed, 22 insertions(+) + +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -1098,6 +1098,9 @@ int pse_ethtool_get_status(struct pse_co + pcdev = psec->pcdev; + ops = pcdev->ops; + mutex_lock(&pcdev->lock); ++ if (pcdev->pi[psec->id].pw_d) ++ status->pw_d_id = pcdev->pi[psec->id].pw_d->id; ++ + ret = ops->pi_get_admin_state(pcdev, psec->id, &admin_state); + if (ret) + goto out; +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -114,6 +114,7 @@ struct pse_pw_limit_ranges { + /** + * struct ethtool_pse_control_status - PSE control/channel status. + * ++ * @pw_d_id: PSE power domain index. + * @podl_admin_state: operational state of the PoDL PSE + * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState + * @podl_pw_status: power detection status of the PoDL PSE. +@@ -135,6 +136,7 @@ struct pse_pw_limit_ranges { + * ranges + */ + struct ethtool_pse_control_status { ++ u32 pw_d_id; + enum ethtool_podl_pse_admin_state podl_admin_state; + enum ethtool_podl_pse_pw_d_status podl_pw_status; + enum ethtool_c33_pse_admin_state c33_admin_state; +--- a/net/ethtool/pse-pd.c ++++ b/net/ethtool/pse-pd.c +@@ -83,6 +83,8 @@ static int pse_reply_size(const struct e + const struct ethtool_pse_control_status *st = &data->status; + int len = 0; + ++ if (st->pw_d_id) ++ len += nla_total_size(sizeof(u32)); /* _PSE_PW_D_ID */ + if (st->podl_admin_state > 0) + len += nla_total_size(sizeof(u32)); /* _PODL_PSE_ADMIN_STATE */ + if (st->podl_pw_status > 0) +@@ -148,6 +150,11 @@ static int pse_fill_reply(struct sk_buff + const struct pse_reply_data *data = PSE_REPDATA(reply_base); + const struct ethtool_pse_control_status *st = &data->status; + ++ if (st->pw_d_id && ++ nla_put_u32(skb, ETHTOOL_A_PSE_PW_D_ID, ++ st->pw_d_id)) ++ return -EMSGSIZE; ++ + if (st->podl_admin_state > 0 && + nla_put_u32(skb, ETHTOOL_A_PODL_PSE_ADMIN_STATE, + st->podl_admin_state)) diff --git a/target/linux/generic/backport-6.12/626-15-v6.17-net-pse-pd-Add-helper-to-report-hw-enable-status.patch b/target/linux/generic/backport-6.12/626-15-v6.17-net-pse-pd-Add-helper-to-report-hw-enable-status.patch new file mode 100644 index 00000000000..ed512898162 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-15-v6.17-net-pse-pd-Add-helper-to-report-hw-enable-status.patch @@ -0,0 +1,74 @@ +From c394e757dedd9cf947f9ac470d615d28fd2b07d1 Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:05 +0200 +Subject: [PATCH] net: pse-pd: Add helper to report hardware enable status of + the PI + +Refactor code by introducing a helper function to retrieve the hardware +enabled state of the PI, avoiding redundant implementations in the +future. + +Signed-off-by: Kory Maincent (Dent Project) +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-6-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pse_core.c | 36 +++++++++++++++++++++++++---------- + 1 file changed, 26 insertions(+), 10 deletions(-) + +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -272,10 +272,34 @@ static struct net_device *pse_control_ge + return psec->attached_phydev->attached_dev; + } + ++/** ++ * pse_pi_is_hw_enabled - Is PI enabled at the hardware level ++ * @pcdev: a pointer to the PSE controller device ++ * @id: Index of the PI ++ * ++ * Return: 1 if the PI is enabled at the hardware level, 0 if not, and ++ * a failure value on error ++ */ ++static int pse_pi_is_hw_enabled(struct pse_controller_dev *pcdev, int id) ++{ ++ struct pse_admin_state admin_state = {0}; ++ int ret; ++ ++ ret = pcdev->ops->pi_get_admin_state(pcdev, id, &admin_state); ++ if (ret < 0) ++ return ret; ++ ++ /* PI is well enabled at the hardware level */ ++ if (admin_state.podl_admin_state == ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED || ++ admin_state.c33_admin_state == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED) ++ return 1; ++ ++ return 0; ++} ++ + static int pse_pi_is_enabled(struct regulator_dev *rdev) + { + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); +- struct pse_admin_state admin_state = {0}; + const struct pse_controller_ops *ops; + int id, ret; + +@@ -285,15 +309,7 @@ static int pse_pi_is_enabled(struct regu + + id = rdev_get_id(rdev); + mutex_lock(&pcdev->lock); +- ret = ops->pi_get_admin_state(pcdev, id, &admin_state); +- if (ret) +- goto out; +- +- if (admin_state.podl_admin_state == ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED || +- admin_state.c33_admin_state == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED) +- ret = 1; +- +-out: ++ ret = pse_pi_is_hw_enabled(pcdev, id); + mutex_unlock(&pcdev->lock); + + return ret; diff --git a/target/linux/generic/backport-6.12/626-16-v6.17-net-pse-pd-Add-support-for-budget-evaluation-strategies.patch b/target/linux/generic/backport-6.12/626-16-v6.17-net-pse-pd-Add-support-for-budget-evaluation-strategies.patch new file mode 100644 index 00000000000..c95d8f0fee3 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-16-v6.17-net-pse-pd-Add-support-for-budget-evaluation-strategies.patch @@ -0,0 +1,1104 @@ +From ffef61d6d27374542f1bce4452200d9bdd2e1edd Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:06 +0200 +Subject: [PATCH] net: pse-pd: Add support for budget evaluation strategies + +ADAPTED FOR OPENWRT 6.12.67 - Documentation and ethtool_netlink_generated.h changes removed + +This patch introduces the ability to configure the PSE PI budget evaluation +strategies. Budget evaluation strategies is utilized by PSE controllers to +determine which ports to turn off first in scenarios such as power budget +exceedance. + +The pis_prio_max value is used to define the maximum priority level +supported by the controller. Both the current priority and the maximum +priority are exposed to the user through the pse_ethtool_get_status call. + +This patch add support for two mode of budget evaluation strategies. +1. Static Method: + This method involves distributing power based on PD classification. + +2. Dynamic Method: + Budget evaluation strategy based on the current consumption per ports + compared to the total power budget. + +Signed-off-by: Kory Maincent (Dent Project) +Acked-by: Oleksij Rempel +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-7-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pse_core.c | 729 +++++++++++++++++++++++++++++++++- + include/linux/pse-pd/pse.h | 76 ++++ + 2 files changed, 791 insertions(+), 14 deletions(-) + +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -47,11 +47,14 @@ struct pse_control { + * @id: ID of the power domain + * @supply: Power supply the Power Domain + * @refcnt: Number of gets of this pse_power_domain ++ * @budget_eval_strategy: Current power budget evaluation strategy of the ++ * power domain + */ + struct pse_power_domain { + int id; + struct regulator *supply; + struct kref refcnt; ++ u32 budget_eval_strategy; + }; + + static int of_load_single_pse_pi_pairset(struct device_node *node, +@@ -297,6 +300,115 @@ static int pse_pi_is_hw_enabled(struct p + return 0; + } + ++/** ++ * pse_pi_is_admin_enable_pending - Check if PI is in admin enable pending state ++ * which mean the power is not yet being ++ * delivered ++ * @pcdev: a pointer to the PSE controller device ++ * @id: Index of the PI ++ * ++ * Detects if a PI is enabled in software with a PD detected, but the hardware ++ * admin state hasn't been applied yet. ++ * ++ * This function is used in the power delivery and retry mechanisms to determine ++ * which PIs need to have power delivery attempted again. ++ * ++ * Return: true if the PI has admin enable flag set in software but not yet ++ * reflected in the hardware admin state, false otherwise. ++ */ ++static bool ++pse_pi_is_admin_enable_pending(struct pse_controller_dev *pcdev, int id) ++{ ++ int ret; ++ ++ /* PI not enabled or nothing is plugged */ ++ if (!pcdev->pi[id].admin_state_enabled || ++ !pcdev->pi[id].isr_pd_detected) ++ return false; ++ ++ ret = pse_pi_is_hw_enabled(pcdev, id); ++ /* PSE PI is already enabled at hardware level */ ++ if (ret == 1) ++ return false; ++ ++ return true; ++} ++ ++static int _pse_pi_delivery_power_sw_pw_ctrl(struct pse_controller_dev *pcdev, ++ int id, ++ struct netlink_ext_ack *extack); ++ ++/** ++ * pse_pw_d_retry_power_delivery - Retry power delivery for pending ports in a ++ * PSE power domain ++ * @pcdev: a pointer to the PSE controller device ++ * @pw_d: a pointer to the PSE power domain ++ * ++ * Scans all ports in the specified power domain and attempts to enable power ++ * delivery to any ports that have admin enable state set but don't yet have ++ * hardware power enabled. Used when there are changes in connection status, ++ * admin state, or priority that might allow previously unpowered ports to ++ * receive power, especially in over-budget conditions. ++ */ ++static void pse_pw_d_retry_power_delivery(struct pse_controller_dev *pcdev, ++ struct pse_power_domain *pw_d) ++{ ++ int i, ret = 0; ++ ++ for (i = 0; i < pcdev->nr_lines; i++) { ++ int prio_max = pcdev->nr_lines; ++ struct netlink_ext_ack extack; ++ ++ if (pcdev->pi[i].pw_d != pw_d) ++ continue; ++ ++ if (!pse_pi_is_admin_enable_pending(pcdev, i)) ++ continue; ++ ++ /* Do not try to enable PI with a lower prio (higher value) ++ * than one which already can't be enabled. ++ */ ++ if (pcdev->pi[i].prio > prio_max) ++ continue; ++ ++ ret = _pse_pi_delivery_power_sw_pw_ctrl(pcdev, i, &extack); ++ if (ret == -ERANGE) ++ prio_max = pcdev->pi[i].prio; ++ } ++} ++ ++/** ++ * pse_pw_d_is_sw_pw_control - Determine if power control is software managed ++ * @pcdev: a pointer to the PSE controller device ++ * @pw_d: a pointer to the PSE power domain ++ * ++ * This function determines whether the power control for a specific power ++ * domain is managed by software in the interrupt handler rather than directly ++ * by hardware. ++ * ++ * Software power control is active in the following cases: ++ * - When the budget evaluation strategy is set to static ++ * - When the budget evaluation strategy is disabled but the PSE controller ++ * has an interrupt handler that can report if a Powered Device is connected ++ * ++ * Return: true if the power control of the power domain is managed by software, ++ * false otherwise ++ */ ++static bool pse_pw_d_is_sw_pw_control(struct pse_controller_dev *pcdev, ++ struct pse_power_domain *pw_d) ++{ ++ if (!pw_d) ++ return false; ++ ++ if (pw_d->budget_eval_strategy == PSE_BUDGET_EVAL_STRAT_STATIC) ++ return true; ++ if (pw_d->budget_eval_strategy == PSE_BUDGET_EVAL_STRAT_DISABLED && ++ pcdev->ops->pi_enable && pcdev->irq) ++ return true; ++ ++ return false; ++} ++ + static int pse_pi_is_enabled(struct regulator_dev *rdev) + { + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); +@@ -309,17 +421,252 @@ static int pse_pi_is_enabled(struct regu + + id = rdev_get_id(rdev); + mutex_lock(&pcdev->lock); ++ if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) { ++ ret = pcdev->pi[id].admin_state_enabled; ++ goto out; ++ } ++ + ret = pse_pi_is_hw_enabled(pcdev, id); ++ ++out: + mutex_unlock(&pcdev->lock); + + return ret; + } + ++/** ++ * pse_pi_deallocate_pw_budget - Deallocate power budget of the PI ++ * @pi: a pointer to the PSE PI ++ */ ++static void pse_pi_deallocate_pw_budget(struct pse_pi *pi) ++{ ++ if (!pi->pw_d || !pi->pw_allocated_mW) ++ return; ++ ++ regulator_free_power_budget(pi->pw_d->supply, pi->pw_allocated_mW); ++ pi->pw_allocated_mW = 0; ++} ++ ++/** ++ * _pse_pi_disable - Call disable operation. Assumes the PSE lock has been ++ * acquired. ++ * @pcdev: a pointer to the PSE ++ * @id: index of the PSE control ++ * ++ * Return: 0 on success and failure value on error ++ */ ++static int _pse_pi_disable(struct pse_controller_dev *pcdev, int id) ++{ ++ const struct pse_controller_ops *ops = pcdev->ops; ++ int ret; ++ ++ if (!ops->pi_disable) ++ return -EOPNOTSUPP; ++ ++ ret = ops->pi_disable(pcdev, id); ++ if (ret) ++ return ret; ++ ++ pse_pi_deallocate_pw_budget(&pcdev->pi[id]); ++ ++ if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) ++ pse_pw_d_retry_power_delivery(pcdev, pcdev->pi[id].pw_d); ++ ++ return 0; ++} ++ ++/** ++ * pse_disable_pi_pol - Disable a PI on a power budget policy ++ * @pcdev: a pointer to the PSE ++ * @id: index of the PSE PI ++ * ++ * Return: 0 on success and failure value on error ++ */ ++static int pse_disable_pi_pol(struct pse_controller_dev *pcdev, int id) ++{ ++ unsigned long notifs = ETHTOOL_PSE_EVENT_OVER_BUDGET; ++ struct pse_ntf ntf = {}; ++ int ret; ++ ++ dev_dbg(pcdev->dev, "Disabling PI %d to free power budget\n", id); ++ ++ ret = _pse_pi_disable(pcdev, id); ++ if (ret) ++ notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR; ++ ++ ntf.notifs = notifs; ++ ntf.id = id; ++ kfifo_in_spinlocked(&pcdev->ntf_fifo, &ntf, 1, &pcdev->ntf_fifo_lock); ++ schedule_work(&pcdev->ntf_work); ++ ++ return ret; ++} ++ ++/** ++ * pse_disable_pi_prio - Disable all PIs of a given priority inside a PSE ++ * power domain ++ * @pcdev: a pointer to the PSE ++ * @pw_d: a pointer to the PSE power domain ++ * @prio: priority ++ * ++ * Return: 0 on success and failure value on error ++ */ ++static int pse_disable_pi_prio(struct pse_controller_dev *pcdev, ++ struct pse_power_domain *pw_d, ++ int prio) ++{ ++ int i; ++ ++ for (i = 0; i < pcdev->nr_lines; i++) { ++ int ret; ++ ++ if (pcdev->pi[i].prio != prio || ++ pcdev->pi[i].pw_d != pw_d || ++ pse_pi_is_hw_enabled(pcdev, i) <= 0) ++ continue; ++ ++ ret = pse_disable_pi_pol(pcdev, i); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * pse_pi_allocate_pw_budget_static_prio - Allocate power budget for the PI ++ * when the budget eval strategy is ++ * static ++ * @pcdev: a pointer to the PSE ++ * @id: index of the PSE control ++ * @pw_req: power requested in mW ++ * @extack: extack for error reporting ++ * ++ * Allocates power using static budget evaluation strategy, where allocation ++ * is based on PD classification. When insufficient budget is available, ++ * lower-priority ports (higher priority numbers) are turned off first. ++ * ++ * Return: 0 on success and failure value on error ++ */ ++static int ++pse_pi_allocate_pw_budget_static_prio(struct pse_controller_dev *pcdev, int id, ++ int pw_req, struct netlink_ext_ack *extack) ++{ ++ struct pse_pi *pi = &pcdev->pi[id]; ++ int ret, _prio; ++ ++ _prio = pcdev->nr_lines; ++ while (regulator_request_power_budget(pi->pw_d->supply, pw_req) == -ERANGE) { ++ if (_prio <= pi->prio) { ++ NL_SET_ERR_MSG_FMT(extack, ++ "PI %d: not enough power budget available", ++ id); ++ return -ERANGE; ++ } ++ ++ ret = pse_disable_pi_prio(pcdev, pi->pw_d, _prio); ++ if (ret < 0) ++ return ret; ++ ++ _prio--; ++ } ++ ++ pi->pw_allocated_mW = pw_req; ++ return 0; ++} ++ ++/** ++ * pse_pi_allocate_pw_budget - Allocate power budget for the PI ++ * @pcdev: a pointer to the PSE ++ * @id: index of the PSE control ++ * @pw_req: power requested in mW ++ * @extack: extack for error reporting ++ * ++ * Return: 0 on success and failure value on error ++ */ ++static int pse_pi_allocate_pw_budget(struct pse_controller_dev *pcdev, int id, ++ int pw_req, struct netlink_ext_ack *extack) ++{ ++ struct pse_pi *pi = &pcdev->pi[id]; ++ ++ if (!pi->pw_d) ++ return 0; ++ ++ /* PSE_BUDGET_EVAL_STRAT_STATIC */ ++ if (pi->pw_d->budget_eval_strategy == PSE_BUDGET_EVAL_STRAT_STATIC) ++ return pse_pi_allocate_pw_budget_static_prio(pcdev, id, pw_req, ++ extack); ++ ++ return 0; ++} ++ ++/** ++ * _pse_pi_delivery_power_sw_pw_ctrl - Enable PSE PI in case of software power ++ * control. Assumes the PSE lock has been ++ * acquired. ++ * @pcdev: a pointer to the PSE ++ * @id: index of the PSE control ++ * @extack: extack for error reporting ++ * ++ * Return: 0 on success and failure value on error ++ */ ++static int _pse_pi_delivery_power_sw_pw_ctrl(struct pse_controller_dev *pcdev, ++ int id, ++ struct netlink_ext_ack *extack) ++{ ++ const struct pse_controller_ops *ops = pcdev->ops; ++ struct pse_pi *pi = &pcdev->pi[id]; ++ int ret, pw_req; ++ ++ if (!ops->pi_get_pw_req) { ++ /* No power allocation management */ ++ ret = ops->pi_enable(pcdev, id); ++ if (ret) ++ NL_SET_ERR_MSG_FMT(extack, ++ "PI %d: enable error %d", ++ id, ret); ++ return ret; ++ } ++ ++ ret = ops->pi_get_pw_req(pcdev, id); ++ if (ret < 0) ++ return ret; ++ ++ pw_req = ret; ++ ++ /* Compare requested power with port power limit and use the lowest ++ * one. ++ */ ++ if (ops->pi_get_pw_limit) { ++ ret = ops->pi_get_pw_limit(pcdev, id); ++ if (ret < 0) ++ return ret; ++ ++ if (ret < pw_req) ++ pw_req = ret; ++ } ++ ++ ret = pse_pi_allocate_pw_budget(pcdev, id, pw_req, extack); ++ if (ret) ++ return ret; ++ ++ ret = ops->pi_enable(pcdev, id); ++ if (ret) { ++ pse_pi_deallocate_pw_budget(pi); ++ NL_SET_ERR_MSG_FMT(extack, ++ "PI %d: enable error %d", ++ id, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int pse_pi_enable(struct regulator_dev *rdev) + { + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); + const struct pse_controller_ops *ops; +- int id, ret; ++ int id, ret = 0; + + ops = pcdev->ops; + if (!ops->pi_enable) +@@ -327,6 +674,23 @@ static int pse_pi_enable(struct regulato + + id = rdev_get_id(rdev); + mutex_lock(&pcdev->lock); ++ if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) { ++ /* Manage enabled status by software. ++ * Real enable process will happen if a port is connected. ++ */ ++ if (pcdev->pi[id].isr_pd_detected) { ++ struct netlink_ext_ack extack; ++ ++ ret = _pse_pi_delivery_power_sw_pw_ctrl(pcdev, id, &extack); ++ } ++ if (!ret || ret == -ERANGE) { ++ pcdev->pi[id].admin_state_enabled = 1; ++ ret = 0; ++ } ++ mutex_unlock(&pcdev->lock); ++ return ret; ++ } ++ + ret = ops->pi_enable(pcdev, id); + if (!ret) + pcdev->pi[id].admin_state_enabled = 1; +@@ -338,21 +702,18 @@ static int pse_pi_enable(struct regulato + static int pse_pi_disable(struct regulator_dev *rdev) + { + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); +- const struct pse_controller_ops *ops; ++ struct pse_pi *pi; + int id, ret; + +- ops = pcdev->ops; +- if (!ops->pi_disable) +- return -EOPNOTSUPP; +- + id = rdev_get_id(rdev); ++ pi = &pcdev->pi[id]; + mutex_lock(&pcdev->lock); +- ret = ops->pi_disable(pcdev, id); ++ ret = _pse_pi_disable(pcdev, id); + if (!ret) +- pcdev->pi[id].admin_state_enabled = 0; +- mutex_unlock(&pcdev->lock); ++ pi->admin_state_enabled = 0; + +- return ret; ++ mutex_unlock(&pcdev->lock); ++ return 0; + } + + static int _pse_pi_get_voltage(struct regulator_dev *rdev) +@@ -628,6 +989,11 @@ static int pse_register_pw_ds(struct pse + } + + pw_d->supply = supply; ++ if (pcdev->supp_budget_eval_strategies) ++ pw_d->budget_eval_strategy = pcdev->supp_budget_eval_strategies; ++ else ++ pw_d->budget_eval_strategy = PSE_BUDGET_EVAL_STRAT_DISABLED; ++ kref_init(&pw_d->refcnt); + pcdev->pi[i].pw_d = pw_d; + } + +@@ -637,6 +1003,34 @@ out: + } + + /** ++ * pse_send_ntf_worker - Worker to send PSE notifications ++ * @work: work object ++ * ++ * Manage and send PSE netlink notifications using a workqueue to avoid ++ * deadlock between pcdev_lock and pse_list_mutex. ++ */ ++static void pse_send_ntf_worker(struct work_struct *work) ++{ ++ struct pse_controller_dev *pcdev; ++ struct pse_ntf ntf; ++ ++ pcdev = container_of(work, struct pse_controller_dev, ntf_work); ++ ++ while (kfifo_out(&pcdev->ntf_fifo, &ntf, 1)) { ++ struct net_device *netdev; ++ struct pse_control *psec; ++ ++ psec = pse_control_find_by_id(pcdev, ntf.id); ++ rtnl_lock(); ++ netdev = pse_control_get_netdev(psec); ++ if (netdev) ++ ethnl_pse_send_ntf(netdev, ntf.notifs); ++ rtnl_unlock(); ++ pse_control_put(psec); ++ } ++} ++ ++/** + * pse_controller_register - register a PSE controller device + * @pcdev: a pointer to the initialized PSE controller device + * +@@ -649,6 +1043,13 @@ int pse_controller_register(struct pse_c + + mutex_init(&pcdev->lock); + INIT_LIST_HEAD(&pcdev->pse_control_head); ++ spin_lock_init(&pcdev->ntf_fifo_lock); ++ ret = kfifo_alloc(&pcdev->ntf_fifo, pcdev->nr_lines, GFP_KERNEL); ++ if (ret) { ++ dev_err(pcdev->dev, "failed to allocate kfifo notifications\n"); ++ return ret; ++ } ++ INIT_WORK(&pcdev->ntf_work, pse_send_ntf_worker); + + if (!pcdev->nr_lines) + pcdev->nr_lines = 1; +@@ -715,6 +1116,10 @@ void pse_controller_unregister(struct ps + { + pse_flush_pw_ds(pcdev); + pse_release_pis(pcdev); ++ if (pcdev->irq) ++ disable_irq(pcdev->irq); ++ cancel_work_sync(&pcdev->ntf_work); ++ kfifo_free(&pcdev->ntf_fifo); + mutex_lock(&pse_list_mutex); + list_del(&pcdev->list); + mutex_unlock(&pse_list_mutex); +@@ -787,6 +1192,52 @@ static unsigned long pse_to_regulator_no + } + + /** ++ * pse_set_config_isr - Set PSE control config according to the PSE ++ * notifications ++ * @pcdev: a pointer to the PSE ++ * @id: index of the PSE control ++ * @notifs: PSE event notifications ++ * ++ * Return: 0 on success and failure value on error ++ */ ++static int pse_set_config_isr(struct pse_controller_dev *pcdev, int id, ++ unsigned long notifs) ++{ ++ int ret = 0; ++ ++ if (notifs & PSE_BUDGET_EVAL_STRAT_DYNAMIC) ++ return 0; ++ ++ if ((notifs & ETHTOOL_C33_PSE_EVENT_DISCONNECTION) && ++ ((notifs & ETHTOOL_C33_PSE_EVENT_DETECTION) || ++ (notifs & ETHTOOL_C33_PSE_EVENT_CLASSIFICATION))) { ++ dev_dbg(pcdev->dev, ++ "PI %d: error, connection and disconnection reported simultaneously", ++ id); ++ return -EINVAL; ++ } ++ ++ if (notifs & ETHTOOL_C33_PSE_EVENT_CLASSIFICATION) { ++ struct netlink_ext_ack extack; ++ ++ pcdev->pi[id].isr_pd_detected = true; ++ if (pcdev->pi[id].admin_state_enabled) { ++ ret = _pse_pi_delivery_power_sw_pw_ctrl(pcdev, id, ++ &extack); ++ if (ret == -ERANGE) ++ ret = 0; ++ } ++ } else if (notifs & ETHTOOL_C33_PSE_EVENT_DISCONNECTION) { ++ if (pcdev->pi[id].admin_state_enabled && ++ pcdev->pi[id].isr_pd_detected) ++ ret = _pse_pi_disable(pcdev, id); ++ pcdev->pi[id].isr_pd_detected = false; ++ } ++ ++ return ret; ++} ++ ++/** + * pse_isr - IRQ handler for PSE + * @irq: irq number + * @data: pointer to user interrupt structure +@@ -808,36 +1259,42 @@ static irqreturn_t pse_isr(int irq, void + memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs)); + mutex_lock(&pcdev->lock); + ret = desc->map_event(irq, pcdev, h->notifs, ¬ifs_mask); +- mutex_unlock(&pcdev->lock); +- if (ret || !notifs_mask) ++ if (ret || !notifs_mask) { ++ mutex_unlock(&pcdev->lock); + return IRQ_NONE; ++ } + + for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) { + unsigned long notifs, rnotifs; +- struct net_device *netdev; +- struct pse_control *psec; ++ struct pse_ntf ntf = {}; + + /* Do nothing PI not described */ + if (!pcdev->pi[i].rdev) + continue; + + notifs = h->notifs[i]; ++ if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[i].pw_d)) { ++ ret = pse_set_config_isr(pcdev, i, notifs); ++ if (ret) ++ notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR; ++ } ++ + dev_dbg(h->pcdev->dev, + "Sending PSE notification EVT 0x%lx\n", notifs); + +- psec = pse_control_find_by_id(pcdev, i); +- rtnl_lock(); +- netdev = pse_control_get_netdev(psec); +- if (netdev) +- ethnl_pse_send_ntf(netdev, notifs); +- rtnl_unlock(); +- pse_control_put(psec); ++ ntf.notifs = notifs; ++ ntf.id = i; ++ kfifo_in_spinlocked(&pcdev->ntf_fifo, &ntf, 1, ++ &pcdev->ntf_fifo_lock); ++ schedule_work(&pcdev->ntf_work); + + rnotifs = pse_to_regulator_notifs(notifs); + regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs, + NULL); + } + ++ mutex_unlock(&pcdev->lock); ++ + return IRQ_HANDLED; + } + +@@ -960,6 +1417,20 @@ pse_control_get_internal(struct pse_cont + goto free_psec; + } + ++ if (!pcdev->ops->pi_get_admin_state) { ++ ret = -EOPNOTSUPP; ++ goto free_psec; ++ } ++ ++ /* Initialize admin_state_enabled before the regulator_get. This ++ * aims to have the right value reported in the first is_enabled ++ * call in case of control managed by software. ++ */ ++ ret = pse_pi_is_hw_enabled(pcdev, index); ++ if (ret < 0) ++ goto free_psec; ++ ++ pcdev->pi[index].admin_state_enabled = ret; + psec->ps = devm_regulator_get_exclusive(pcdev->dev, + rdev_get_name(pcdev->pi[index].rdev)); + if (IS_ERR(psec->ps)) { +@@ -967,12 +1438,6 @@ pse_control_get_internal(struct pse_cont + goto put_module; + } + +- ret = regulator_is_enabled(psec->ps); +- if (ret < 0) +- goto regulator_put; +- +- pcdev->pi[index].admin_state_enabled = ret; +- + psec->pcdev = pcdev; + list_add(&psec->list, &pcdev->pse_control_head); + psec->id = index; +@@ -981,8 +1446,6 @@ pse_control_get_internal(struct pse_cont + + return psec; + +-regulator_put: +- devm_regulator_put(psec->ps); + put_module: + module_put(pcdev->owner); + free_psec: +@@ -1094,6 +1557,35 @@ out: + EXPORT_SYMBOL_GPL(of_pse_control_get); + + /** ++ * pse_get_sw_admin_state - Convert the software admin state to c33 or podl ++ * admin state value used in the standard ++ * @psec: PSE control pointer ++ * @admin_state: a pointer to the admin_state structure ++ */ ++static void pse_get_sw_admin_state(struct pse_control *psec, ++ struct pse_admin_state *admin_state) ++{ ++ struct pse_pi *pi = &psec->pcdev->pi[psec->id]; ++ ++ if (pse_has_podl(psec)) { ++ if (pi->admin_state_enabled) ++ admin_state->podl_admin_state = ++ ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED; ++ else ++ admin_state->podl_admin_state = ++ ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED; ++ } ++ if (pse_has_c33(psec)) { ++ if (pi->admin_state_enabled) ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; ++ else ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; ++ } ++} ++ ++/** + * pse_ethtool_get_status - get status of PSE control + * @psec: PSE control pointer + * @extack: extack for reporting useful error messages +@@ -1109,19 +1601,46 @@ int pse_ethtool_get_status(struct pse_co + struct pse_pw_status pw_status = {0}; + const struct pse_controller_ops *ops; + struct pse_controller_dev *pcdev; ++ struct pse_pi *pi; + int ret; + + pcdev = psec->pcdev; + ops = pcdev->ops; ++ ++ pi = &pcdev->pi[psec->id]; + mutex_lock(&pcdev->lock); +- if (pcdev->pi[psec->id].pw_d) +- status->pw_d_id = pcdev->pi[psec->id].pw_d->id; ++ if (pi->pw_d) { ++ status->pw_d_id = pi->pw_d->id; ++ if (pse_pw_d_is_sw_pw_control(pcdev, pi->pw_d)) { ++ pse_get_sw_admin_state(psec, &admin_state); ++ } else { ++ ret = ops->pi_get_admin_state(pcdev, psec->id, ++ &admin_state); ++ if (ret) ++ goto out; ++ } ++ status->podl_admin_state = admin_state.podl_admin_state; ++ status->c33_admin_state = admin_state.c33_admin_state; + +- ret = ops->pi_get_admin_state(pcdev, psec->id, &admin_state); +- if (ret) +- goto out; +- status->podl_admin_state = admin_state.podl_admin_state; +- status->c33_admin_state = admin_state.c33_admin_state; ++ switch (pi->pw_d->budget_eval_strategy) { ++ case PSE_BUDGET_EVAL_STRAT_STATIC: ++ status->prio_max = pcdev->nr_lines - 1; ++ status->prio = pi->prio; ++ break; ++ case PSE_BUDGET_EVAL_STRAT_DYNAMIC: ++ status->prio_max = pcdev->pis_prio_max; ++ if (ops->pi_get_prio) { ++ ret = ops->pi_get_prio(pcdev, psec->id); ++ if (ret < 0) ++ goto out; ++ ++ status->prio = ret; ++ } ++ break; ++ default: ++ break; ++ } ++ } + + ret = ops->pi_get_pw_status(pcdev, psec->id, &pw_status); + if (ret) +@@ -1271,6 +1790,52 @@ int pse_ethtool_set_config(struct pse_co + EXPORT_SYMBOL_GPL(pse_ethtool_set_config); + + /** ++ * pse_pi_update_pw_budget - Update PSE power budget allocated with new ++ * power in mW ++ * @pcdev: a pointer to the PSE controller device ++ * @id: index of the PSE PI ++ * @pw_req: power requested ++ * @extack: extack for reporting useful error messages ++ * ++ * Return: Previous power allocated on success and failure value on error ++ */ ++static int pse_pi_update_pw_budget(struct pse_controller_dev *pcdev, int id, ++ const unsigned int pw_req, ++ struct netlink_ext_ack *extack) ++{ ++ struct pse_pi *pi = &pcdev->pi[id]; ++ int previous_pw_allocated; ++ int pw_diff, ret = 0; ++ ++ /* We don't want pw_allocated_mW value change in the middle of an ++ * power budget update ++ */ ++ mutex_lock(&pcdev->lock); ++ previous_pw_allocated = pi->pw_allocated_mW; ++ pw_diff = pw_req - previous_pw_allocated; ++ if (!pw_diff) { ++ goto out; ++ } else if (pw_diff > 0) { ++ ret = regulator_request_power_budget(pi->pw_d->supply, pw_diff); ++ if (ret) { ++ NL_SET_ERR_MSG_FMT(extack, ++ "PI %d: not enough power budget available", ++ id); ++ goto out; ++ } ++ ++ } else { ++ regulator_free_power_budget(pi->pw_d->supply, -pw_diff); ++ } ++ pi->pw_allocated_mW = pw_req; ++ ret = previous_pw_allocated; ++ ++out: ++ mutex_unlock(&pcdev->lock); ++ return ret; ++} ++ ++/** + * pse_ethtool_set_pw_limit - set PSE control power limit + * @psec: PSE control pointer + * @extack: extack for reporting useful error messages +@@ -1282,7 +1847,7 @@ int pse_ethtool_set_pw_limit(struct pse_ + struct netlink_ext_ack *extack, + const unsigned int pw_limit) + { +- int uV, uA, ret; ++ int uV, uA, ret, previous_pw_allocated = 0; + s64 tmp_64; + + if (pw_limit > MAX_PI_PW) +@@ -1306,10 +1871,100 @@ int pse_ethtool_set_pw_limit(struct pse_ + /* uA = mW * 1000000000 / uV */ + uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV); + +- return regulator_set_current_limit(psec->ps, 0, uA); ++ /* Update power budget only in software power control case and ++ * if a Power Device is powered. ++ */ ++ if (pse_pw_d_is_sw_pw_control(psec->pcdev, ++ psec->pcdev->pi[psec->id].pw_d) && ++ psec->pcdev->pi[psec->id].admin_state_enabled && ++ psec->pcdev->pi[psec->id].isr_pd_detected) { ++ ret = pse_pi_update_pw_budget(psec->pcdev, psec->id, ++ pw_limit, extack); ++ if (ret < 0) ++ return ret; ++ previous_pw_allocated = ret; ++ } ++ ++ ret = regulator_set_current_limit(psec->ps, 0, uA); ++ if (ret < 0 && previous_pw_allocated) { ++ pse_pi_update_pw_budget(psec->pcdev, psec->id, ++ previous_pw_allocated, extack); ++ } ++ ++ return ret; + } + EXPORT_SYMBOL_GPL(pse_ethtool_set_pw_limit); + ++/** ++ * pse_ethtool_set_prio - Set PSE PI priority according to the budget ++ * evaluation strategy ++ * @psec: PSE control pointer ++ * @extack: extack for reporting useful error messages ++ * @prio: priovity value ++ * ++ * Return: 0 on success and failure value on error ++ */ ++int pse_ethtool_set_prio(struct pse_control *psec, ++ struct netlink_ext_ack *extack, ++ unsigned int prio) ++{ ++ struct pse_controller_dev *pcdev = psec->pcdev; ++ const struct pse_controller_ops *ops; ++ int ret = 0; ++ ++ if (!pcdev->pi[psec->id].pw_d) { ++ NL_SET_ERR_MSG(extack, "no power domain attached"); ++ return -EOPNOTSUPP; ++ } ++ ++ /* We don't want priority change in the middle of an ++ * enable/disable call or a priority mode change ++ */ ++ mutex_lock(&pcdev->lock); ++ switch (pcdev->pi[psec->id].pw_d->budget_eval_strategy) { ++ case PSE_BUDGET_EVAL_STRAT_STATIC: ++ if (prio >= pcdev->nr_lines) { ++ NL_SET_ERR_MSG_FMT(extack, ++ "priority %d exceed priority max %d", ++ prio, pcdev->nr_lines); ++ ret = -ERANGE; ++ goto out; ++ } ++ ++ pcdev->pi[psec->id].prio = prio; ++ pse_pw_d_retry_power_delivery(pcdev, pcdev->pi[psec->id].pw_d); ++ break; ++ ++ case PSE_BUDGET_EVAL_STRAT_DYNAMIC: ++ ops = psec->pcdev->ops; ++ if (!ops->pi_set_prio) { ++ NL_SET_ERR_MSG(extack, ++ "pse driver does not support setting port priority"); ++ ret = -EOPNOTSUPP; ++ goto out; ++ } ++ ++ if (prio > pcdev->pis_prio_max) { ++ NL_SET_ERR_MSG_FMT(extack, ++ "priority %d exceed priority max %d", ++ prio, pcdev->pis_prio_max); ++ ret = -ERANGE; ++ goto out; ++ } ++ ++ ret = ops->pi_set_prio(pcdev, psec->id, prio); ++ break; ++ ++ default: ++ ret = -EOPNOTSUPP; ++ } ++ ++out: ++ mutex_unlock(&pcdev->lock); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(pse_ethtool_set_prio); ++ + bool pse_has_podl(struct pse_control *psec) + { + return psec->pcdev->types & ETHTOOL_PSE_PODL; +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -6,6 +6,8 @@ + #define _LINUX_PSE_CONTROLLER_H + + #include ++#include ++#include + #include + #include + #include +@@ -134,6 +136,9 @@ struct pse_pw_limit_ranges { + * is in charge of the memory allocation + * @c33_pw_limit_nb_ranges: number of supported power limit configuration + * ranges ++ * @prio_max: max priority allowed for the c33_prio variable value. ++ * @prio: priority of the PSE. Managed by PSE core in case of static budget ++ * evaluation strategy. + */ + struct ethtool_pse_control_status { + u32 pw_d_id; +@@ -147,6 +152,8 @@ struct ethtool_pse_control_status { + u32 c33_avail_pw_limit; + struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; + u32 c33_pw_limit_nb_ranges; ++ u32 prio_max; ++ u32 prio; + }; + + /** +@@ -170,6 +177,11 @@ struct ethtool_pse_control_status { + * range. The driver is in charge of the memory + * allocation and should return the number of + * ranges. ++ * @pi_get_prio: Get the PSE PI priority. ++ * @pi_set_prio: Configure the PSE PI priority. ++ * @pi_get_pw_req: Get the power requested by a PD before enabling the PSE PI. ++ * This is only relevant when an interrupt is registered using ++ * devm_pse_irq_helper helper. + */ + struct pse_controller_ops { + int (*setup_pi_matrix)(struct pse_controller_dev *pcdev); +@@ -190,6 +202,10 @@ struct pse_controller_ops { + int id, int max_mW); + int (*pi_get_pw_limit_ranges)(struct pse_controller_dev *pcdev, int id, + struct pse_pw_limit_ranges *pw_limit_ranges); ++ int (*pi_get_prio)(struct pse_controller_dev *pcdev, int id); ++ int (*pi_set_prio)(struct pse_controller_dev *pcdev, int id, ++ unsigned int prio); ++ int (*pi_get_pw_req)(struct pse_controller_dev *pcdev, int id); + }; + + struct module; +@@ -225,6 +241,13 @@ struct pse_pi_pairset { + * @rdev: regulator represented by the PSE PI + * @admin_state_enabled: PI enabled state + * @pw_d: Power domain of the PSE PI ++ * @prio: Priority of the PSE PI. Used in static budget evaluation strategy ++ * @isr_pd_detected: PSE PI detection status managed by the interruption ++ * handler. This variable is relevant when the power enabled ++ * management is managed in software like the static ++ * budget evaluation strategy. ++ * @pw_allocated_mW: Power allocated to a PSE PI to manage power budget in ++ * static budget evaluation strategy. + */ + struct pse_pi { + struct pse_pi_pairset pairset[2]; +@@ -232,6 +255,20 @@ struct pse_pi { + struct regulator_dev *rdev; + bool admin_state_enabled; + struct pse_power_domain *pw_d; ++ int prio; ++ bool isr_pd_detected; ++ int pw_allocated_mW; ++}; ++ ++/** ++ * struct pse_ntf - PSE notification element ++ * ++ * @id: ID of the PSE control ++ * @notifs: PSE notifications to be reported ++ */ ++struct pse_ntf { ++ int id; ++ unsigned long notifs; + }; + + /** +@@ -249,6 +286,12 @@ struct pse_pi { + * @pi: table of PSE PIs described in this controller device + * @no_of_pse_pi: flag set if the pse_pis devicetree node is not used + * @irq: PSE interrupt ++ * @pis_prio_max: Maximum value allowed for the PSE PIs priority ++ * @supp_budget_eval_strategies: budget evaluation strategies supported ++ * by the PSE ++ * @ntf_work: workqueue for PSE notification management ++ * @ntf_fifo: PSE notifications FIFO ++ * @ntf_fifo_lock: protect @ntf_fifo writer + */ + struct pse_controller_dev { + const struct pse_controller_ops *ops; +@@ -263,6 +306,29 @@ struct pse_controller_dev { + struct pse_pi *pi; + bool no_of_pse_pi; + int irq; ++ unsigned int pis_prio_max; ++ u32 supp_budget_eval_strategies; ++ struct work_struct ntf_work; ++ DECLARE_KFIFO_PTR(ntf_fifo, struct pse_ntf); ++ spinlock_t ntf_fifo_lock; /* Protect @ntf_fifo writer */ ++}; ++ ++/** ++ * enum pse_budget_eval_strategies - PSE budget evaluation strategies. ++ * @PSE_BUDGET_EVAL_STRAT_DISABLED: Budget evaluation strategy disabled. ++ * @PSE_BUDGET_EVAL_STRAT_STATIC: PSE static budget evaluation strategy. ++ * Budget evaluation strategy based on the power requested during PD ++ * classification. This strategy is managed by the PSE core. ++ * @PSE_BUDGET_EVAL_STRAT_DYNAMIC: PSE dynamic budget evaluation ++ * strategy. Budget evaluation strategy based on the current consumption ++ * per ports compared to the total power budget. This mode is managed by ++ * the PSE controller. ++ */ ++ ++enum pse_budget_eval_strategies { ++ PSE_BUDGET_EVAL_STRAT_DISABLED = 1 << 0, ++ PSE_BUDGET_EVAL_STRAT_STATIC = 1 << 1, ++ PSE_BUDGET_EVAL_STRAT_DYNAMIC = 1 << 2, + }; + + #if IS_ENABLED(CONFIG_PSE_CONTROLLER) +@@ -287,6 +353,9 @@ int pse_ethtool_set_config(struct pse_co + int pse_ethtool_set_pw_limit(struct pse_control *psec, + struct netlink_ext_ack *extack, + const unsigned int pw_limit); ++int pse_ethtool_set_prio(struct pse_control *psec, ++ struct netlink_ext_ack *extack, ++ unsigned int prio); + int pse_ethtool_get_pw_limit(struct pse_control *psec, + struct netlink_ext_ack *extack); + +@@ -331,6 +400,13 @@ static inline int pse_ethtool_get_pw_lim + { + return -EOPNOTSUPP; + } ++ ++static inline int pse_ethtool_set_prio(struct pse_control *psec, ++ struct netlink_ext_ack *extack, ++ unsigned int prio) ++{ ++ return -EOPNOTSUPP; ++} + + static inline bool pse_has_podl(struct pse_control *psec) + { diff --git a/target/linux/generic/backport-6.12/626-16b-v6.17-regulator-Add-support-for-power-budget.patch b/target/linux/generic/backport-6.12/626-16b-v6.17-regulator-Add-support-for-power-budget.patch new file mode 100644 index 00000000000..b77f96db8e1 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-16b-v6.17-regulator-Add-support-for-power-budget.patch @@ -0,0 +1,257 @@ +From 42d7c87b4e1251f36eceac987e74623e7cda8577 Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Wed, 15 Jan 2025 15:41:57 +0100 +Subject: [PATCH] regulator: Add support for power budget + +Introduce power budget management for the regulator device. Enable tracking +of available power capacity by providing helpers to request and release +power budget allocations. + +Signed-off-by: Kory Maincent +Link: https://patch.msgid.link/20250115-feature_regulator_pw_budget-v2-1-0a44b949e6bc@bootlin.com +Signed-off-by: Mark Brown +Signed-off-by: Bevan Weiss +--- + drivers/regulator/core.c | 114 +++++++++++++++++++++++++++++ + drivers/regulator/of_regulator.c | 3 + + include/linux/regulator/consumer.h | 21 ++++++ + include/linux/regulator/driver.h | 2 + + include/linux/regulator/machine.h | 2 + + 5 files changed, 142 insertions(+) + +--- a/drivers/regulator/core.c ++++ b/drivers/regulator/core.c +@@ -916,6 +916,26 @@ static ssize_t bypass_show(struct device + } + static DEVICE_ATTR_RO(bypass); + ++static ssize_t power_budget_milliwatt_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct regulator_dev *rdev = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", rdev->constraints->pw_budget_mW); ++} ++static DEVICE_ATTR_RO(power_budget_milliwatt); ++ ++static ssize_t power_requested_milliwatt_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct regulator_dev *rdev = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", rdev->pw_requested_mW); ++} ++static DEVICE_ATTR_RO(power_requested_milliwatt); ++ + #define REGULATOR_ERROR_ATTR(name, bit) \ + static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +@@ -1148,6 +1168,10 @@ static void print_constraints_debug(stru + if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY) + count += scnprintf(buf + count, len - count, "standby "); + ++ if (constraints->pw_budget_mW) ++ count += scnprintf(buf + count, len - count, "%d mW budget", ++ constraints->pw_budget_mW); ++ + if (!count) + count = scnprintf(buf, len, "no parameters"); + else +@@ -1636,6 +1660,9 @@ static int set_machine_constraints(struc + rdev->last_off = ktime_get(); + } + ++ if (!rdev->constraints->pw_budget_mW) ++ rdev->constraints->pw_budget_mW = INT_MAX; ++ + print_constraints(rdev); + return 0; + } +@@ -4651,6 +4678,87 @@ int regulator_get_current_limit(struct r + EXPORT_SYMBOL_GPL(regulator_get_current_limit); + + /** ++ * regulator_get_unclaimed_power_budget - get regulator unclaimed power budget ++ * @regulator: regulator source ++ * ++ * Return: Unclaimed power budget of the regulator in mW. ++ */ ++int regulator_get_unclaimed_power_budget(struct regulator *regulator) ++{ ++ return regulator->rdev->constraints->pw_budget_mW - ++ regulator->rdev->pw_requested_mW; ++} ++EXPORT_SYMBOL_GPL(regulator_get_unclaimed_power_budget); ++ ++/** ++ * regulator_request_power_budget - request power budget on a regulator ++ * @regulator: regulator source ++ * @pw_req: Power requested ++ * ++ * Return: 0 on success or a negative error number on failure. ++ */ ++int regulator_request_power_budget(struct regulator *regulator, ++ unsigned int pw_req) ++{ ++ struct regulator_dev *rdev = regulator->rdev; ++ int ret = 0, pw_tot_req; ++ ++ regulator_lock(rdev); ++ if (rdev->supply) { ++ ret = regulator_request_power_budget(rdev->supply, pw_req); ++ if (ret < 0) ++ goto out; ++ } ++ ++ pw_tot_req = rdev->pw_requested_mW + pw_req; ++ if (pw_tot_req > rdev->constraints->pw_budget_mW) { ++ rdev_warn(rdev, "power requested %d mW out of budget %d mW", ++ pw_req, ++ rdev->constraints->pw_budget_mW - rdev->pw_requested_mW); ++ regulator_notifier_call_chain(rdev, ++ REGULATOR_EVENT_OVER_CURRENT_WARN, ++ NULL); ++ ret = -ERANGE; ++ goto out; ++ } ++ ++ rdev->pw_requested_mW = pw_tot_req; ++out: ++ regulator_unlock(rdev); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(regulator_request_power_budget); ++ ++/** ++ * regulator_free_power_budget - free power budget on a regulator ++ * @regulator: regulator source ++ * @pw: Power to be released. ++ * ++ * Return: Power budget of the regulator in mW. ++ */ ++void regulator_free_power_budget(struct regulator *regulator, ++ unsigned int pw) ++{ ++ struct regulator_dev *rdev = regulator->rdev; ++ int pw_tot_req; ++ ++ regulator_lock(rdev); ++ if (rdev->supply) ++ regulator_free_power_budget(rdev->supply, pw); ++ ++ pw_tot_req = rdev->pw_requested_mW - pw; ++ if (pw_tot_req >= 0) ++ rdev->pw_requested_mW = pw_tot_req; ++ else ++ rdev_warn(rdev, ++ "too much power freed %d mW (already requested %d mW)", ++ pw, rdev->pw_requested_mW); ++ ++ regulator_unlock(rdev); ++} ++EXPORT_SYMBOL_GPL(regulator_free_power_budget); ++ ++/** + * regulator_set_mode - set regulator operating mode + * @regulator: regulator source + * @mode: operating mode - one of the REGULATOR_MODE constants +@@ -5288,6 +5396,8 @@ static struct attribute *regulator_dev_a + &dev_attr_suspend_standby_mode.attr, + &dev_attr_suspend_mem_mode.attr, + &dev_attr_suspend_disk_mode.attr, ++ &dev_attr_power_budget_milliwatt.attr, ++ &dev_attr_power_requested_milliwatt.attr, + NULL + }; + +@@ -5369,6 +5479,10 @@ static umode_t regulator_attr_is_visible + attr == &dev_attr_suspend_disk_mode.attr) + return ops->set_suspend_mode ? mode : 0; + ++ if (attr == &dev_attr_power_budget_milliwatt.attr || ++ attr == &dev_attr_power_requested_milliwatt.attr) ++ return rdev->constraints->pw_budget_mW != INT_MAX ? mode : 0; ++ + return mode; + } + +--- a/drivers/regulator/of_regulator.c ++++ b/drivers/regulator/of_regulator.c +@@ -125,6 +125,9 @@ static int of_get_regulation_constraints + if (constraints->min_uA != constraints->max_uA) + constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT; + ++ if (!of_property_read_u32(np, "regulator-power-budget-milliwatt", &pval)) ++ constraints->pw_budget_mW = pval; ++ + constraints->boot_on = of_property_read_bool(np, "regulator-boot-on"); + constraints->always_on = of_property_read_bool(np, "regulator-always-on"); + if (!constraints->always_on) /* status change should be possible. */ +--- a/include/linux/regulator/consumer.h ++++ b/include/linux/regulator/consumer.h +@@ -235,6 +235,11 @@ int regulator_sync_voltage(struct regula + int regulator_set_current_limit(struct regulator *regulator, + int min_uA, int max_uA); + int regulator_get_current_limit(struct regulator *regulator); ++int regulator_get_unclaimed_power_budget(struct regulator *regulator); ++int regulator_request_power_budget(struct regulator *regulator, ++ unsigned int pw_req); ++void regulator_free_power_budget(struct regulator *regulator, ++ unsigned int pw); + + int regulator_set_mode(struct regulator *regulator, unsigned int mode); + unsigned int regulator_get_mode(struct regulator *regulator); +@@ -534,6 +539,22 @@ static inline int regulator_get_current_ + return 0; + } + ++static inline int regulator_get_unclaimed_power_budget(struct regulator *regulator) ++{ ++ return INT_MAX; ++} ++ ++static inline int regulator_request_power_budget(struct regulator *regulator, ++ unsigned int pw_req) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline void regulator_free_power_budget(struct regulator *regulator, ++ unsigned int pw) ++{ ++} ++ + static inline int regulator_set_mode(struct regulator *regulator, + unsigned int mode) + { +--- a/include/linux/regulator/driver.h ++++ b/include/linux/regulator/driver.h +@@ -649,6 +649,8 @@ struct regulator_dev { + int cached_err; + bool use_cached_err; + spinlock_t err_lock; ++ ++ int pw_requested_mW; + }; + + /* +--- a/include/linux/regulator/machine.h ++++ b/include/linux/regulator/machine.h +@@ -113,6 +113,7 @@ struct notification_limit { + * @min_uA: Smallest current consumers may set. + * @max_uA: Largest current consumers may set. + * @ilim_uA: Maximum input current. ++ * @pw_budget_mW: Power budget for the regulator in mW. + * @system_load: Load that isn't captured by any consumer requests. + * + * @over_curr_limits: Limits for acting on over current. +@@ -185,6 +186,7 @@ struct regulation_constraints { + int max_uA; + int ilim_uA; + ++ int pw_budget_mW; + int system_load; + + /* used for coupled regulators */ diff --git a/target/linux/generic/backport-6.12/626-17-v6.17-net-pse-pd-pd692x0-Add-PSE-PI-priority-feature.patch b/target/linux/generic/backport-6.12/626-17-v6.17-net-pse-pd-pd692x0-Add-PSE-PI-priority-feature.patch new file mode 100644 index 00000000000..6e1f2d63c9a --- /dev/null +++ b/target/linux/generic/backport-6.12/626-17-v6.17-net-pse-pd-pd692x0-Add-PSE-PI-priority-feature.patch @@ -0,0 +1,326 @@ +From 359754013e6a7fc81af6735ebbfedd4a01999f68 Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:08 +0200 +Subject: [PATCH] net: pse-pd: pd692x0: Add support for PSE PI priority feature + +This patch extends the PSE callbacks by adding support for the newly +introduced pi_set_prio() callback, enabling the configuration of PSE PI +priorities. The current port priority is now also included in the status +information returned to users. + +Signed-off-by: Kory Maincent (Dent Project) +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-9-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pd692x0.c | 205 +++++++++++++++++++++++++++++++++++ + 1 file changed, 205 insertions(+) + +--- a/drivers/net/pse-pd/pd692x0.c ++++ b/drivers/net/pse-pd/pd692x0.c +@@ -12,6 +12,8 @@ + #include + #include + #include ++#include ++#include + + #define PD692X0_PSE_NAME "pd692x0_pse" + +@@ -76,6 +78,8 @@ enum { + PD692X0_MSG_GET_PORT_CLASS, + PD692X0_MSG_GET_PORT_MEAS, + PD692X0_MSG_GET_PORT_PARAM, ++ PD692X0_MSG_GET_POWER_BANK, ++ PD692X0_MSG_SET_POWER_BANK, + + /* add new message above here */ + PD692X0_MSG_CNT +@@ -95,6 +99,8 @@ struct pd692x0_priv { + unsigned long last_cmd_key_time; + + enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS]; ++ struct regulator_dev *manager_reg[PD692X0_MAX_MANAGERS]; ++ int manager_pw_budget[PD692X0_MAX_MANAGERS]; + }; + + /* Template list of communication messages. The non-null bytes defined here +@@ -170,6 +176,16 @@ static const struct pd692x0_msg pd692x0_ + .data = {0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e}, + }, ++ [PD692X0_MSG_GET_POWER_BANK] = { ++ .key = PD692X0_KEY_REQ, ++ .sub = {0x07, 0x0b, 0x57}, ++ .data = { 0, 0x4e, 0x4e, 0x4e, ++ 0x4e, 0x4e, 0x4e, 0x4e}, ++ }, ++ [PD692X0_MSG_SET_POWER_BANK] = { ++ .key = PD692X0_KEY_CMD, ++ .sub = {0x07, 0x0b, 0x57}, ++ }, + }; + + static u8 pd692x0_build_msg(struct pd692x0_msg *msg, u8 echo) +@@ -739,6 +755,29 @@ pd692x0_pi_get_actual_pw(struct pse_cont + return (buf.data[0] << 4 | buf.data[1]) * 100; + } + ++static int ++pd692x0_pi_get_prio(struct pse_controller_dev *pcdev, int id) ++{ ++ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); ++ struct pd692x0_msg msg, buf = {0}; ++ int ret; ++ ++ ret = pd692x0_fw_unavailable(priv); ++ if (ret) ++ return ret; ++ ++ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_PARAM]; ++ msg.sub[2] = id; ++ ret = pd692x0_sendrecv_msg(priv, &msg, &buf); ++ if (ret < 0) ++ return ret; ++ if (!buf.data[2] || buf.data[2] > pcdev->pis_prio_max + 1) ++ return -ERANGE; ++ ++ /* PSE core priority start at 0 */ ++ return buf.data[2] - 1; ++} ++ + static struct pd692x0_msg_ver pd692x0_get_sw_version(struct pd692x0_priv *priv) + { + struct device *dev = &priv->client->dev; +@@ -766,6 +805,7 @@ static struct pd692x0_msg_ver pd692x0_ge + + struct pd692x0_manager { + struct device_node *port_node[PD692X0_MAX_MANAGER_PORTS]; ++ struct device_node *node; + int nports; + }; + +@@ -857,6 +897,8 @@ pd692x0_of_get_managers(struct pd692x0_p + if (ret) + goto out; + ++ of_node_get(node); ++ manager[manager_id].node = node; + nmanagers++; + } + +@@ -869,6 +911,8 @@ out: + of_node_put(manager[i].port_node[j]); + manager[i].port_node[j] = NULL; + } ++ of_node_put(manager[i].node); ++ manager[i].node = NULL; + } + + of_node_put(node); +@@ -876,6 +920,130 @@ out: + return ret; + } + ++static const struct regulator_ops dummy_ops; ++ ++static struct regulator_dev * ++pd692x0_register_manager_regulator(struct device *dev, char *reg_name, ++ struct device_node *node) ++{ ++ struct regulator_init_data *rinit_data; ++ struct regulator_config rconfig = {0}; ++ struct regulator_desc *rdesc; ++ struct regulator_dev *rdev; ++ ++ rinit_data = devm_kzalloc(dev, sizeof(*rinit_data), ++ GFP_KERNEL); ++ if (!rinit_data) ++ return ERR_PTR(-ENOMEM); ++ ++ rdesc = devm_kzalloc(dev, sizeof(*rdesc), GFP_KERNEL); ++ if (!rdesc) ++ return ERR_PTR(-ENOMEM); ++ ++ rdesc->name = reg_name; ++ rdesc->type = REGULATOR_VOLTAGE; ++ rdesc->ops = &dummy_ops; ++ rdesc->owner = THIS_MODULE; ++ ++ rinit_data->supply_regulator = "vmain"; ++ ++ rconfig.dev = dev; ++ rconfig.init_data = rinit_data; ++ rconfig.of_node = node; ++ ++ rdev = devm_regulator_register(dev, rdesc, &rconfig); ++ if (IS_ERR(rdev)) { ++ dev_err_probe(dev, PTR_ERR(rdev), ++ "Failed to register regulator\n"); ++ return rdev; ++ } ++ ++ return rdev; ++} ++ ++static int ++pd692x0_register_managers_regulator(struct pd692x0_priv *priv, ++ const struct pd692x0_manager *manager, ++ int nmanagers) ++{ ++ struct device *dev = &priv->client->dev; ++ size_t reg_name_len; ++ int i; ++ ++ /* Each regulator name len is dev name + 12 char + ++ * int max digit number (10) + 1 ++ */ ++ reg_name_len = strlen(dev_name(dev)) + 23; ++ ++ for (i = 0; i < nmanagers; i++) { ++ struct regulator_dev *rdev; ++ char *reg_name; ++ ++ reg_name = devm_kzalloc(dev, reg_name_len, GFP_KERNEL); ++ if (!reg_name) ++ return -ENOMEM; ++ snprintf(reg_name, 26, "pse-%s-manager%d", dev_name(dev), i); ++ rdev = pd692x0_register_manager_regulator(dev, reg_name, ++ manager[i].node); ++ if (IS_ERR(rdev)) ++ return PTR_ERR(rdev); ++ ++ priv->manager_reg[i] = rdev; ++ } ++ ++ return 0; ++} ++ ++static int ++pd692x0_conf_manager_power_budget(struct pd692x0_priv *priv, int id, int pw) ++{ ++ struct pd692x0_msg msg, buf; ++ int ret, pw_mW = pw / 1000; ++ ++ msg = pd692x0_msg_template_list[PD692X0_MSG_GET_POWER_BANK]; ++ msg.data[0] = id; ++ ret = pd692x0_sendrecv_msg(priv, &msg, &buf); ++ if (ret < 0) ++ return ret; ++ ++ msg = pd692x0_msg_template_list[PD692X0_MSG_SET_POWER_BANK]; ++ msg.data[0] = id; ++ msg.data[1] = pw_mW >> 8; ++ msg.data[2] = pw_mW & 0xff; ++ msg.data[3] = buf.sub[2]; ++ msg.data[4] = buf.data[0]; ++ msg.data[5] = buf.data[1]; ++ msg.data[6] = buf.data[2]; ++ msg.data[7] = buf.data[3]; ++ return pd692x0_sendrecv_msg(priv, &msg, &buf); ++} ++ ++static int ++pd692x0_configure_managers(struct pd692x0_priv *priv, int nmanagers) ++{ ++ int i, ret; ++ ++ for (i = 0; i < nmanagers; i++) { ++ struct regulator *supply = priv->manager_reg[i]->supply; ++ int pw_budget; ++ ++ pw_budget = regulator_get_unclaimed_power_budget(supply); ++ /* Max power budget per manager */ ++ if (pw_budget > 6000000) ++ pw_budget = 6000000; ++ ret = regulator_request_power_budget(supply, pw_budget); ++ if (ret < 0) ++ return ret; ++ ++ priv->manager_pw_budget[i] = pw_budget; ++ ret = pd692x0_conf_manager_power_budget(priv, i, pw_budget); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int + pd692x0_set_port_matrix(const struct pse_pi_pairset *pairset, + const struct pd692x0_manager *manager, +@@ -998,6 +1166,14 @@ static int pd692x0_setup_pi_matrix(struc + return ret; + + nmanagers = ret; ++ ret = pd692x0_register_managers_regulator(priv, manager, nmanagers); ++ if (ret) ++ goto out; ++ ++ ret = pd692x0_configure_managers(priv, nmanagers); ++ if (ret) ++ goto out; ++ + ret = pd692x0_set_ports_matrix(priv, manager, nmanagers, port_matrix); + if (ret) + goto out; +@@ -1008,8 +1184,14 @@ static int pd692x0_setup_pi_matrix(struc + + out: + for (i = 0; i < nmanagers; i++) { ++ struct regulator *supply = priv->manager_reg[i]->supply; ++ ++ regulator_free_power_budget(supply, ++ priv->manager_pw_budget[i]); ++ + for (j = 0; j < manager[i].nports; j++) + of_node_put(manager[i].port_node[j]); ++ of_node_put(manager[i].node); + } + return ret; + } +@@ -1071,6 +1253,25 @@ static int pd692x0_pi_set_pw_limit(struc + return pd692x0_sendrecv_msg(priv, &msg, &buf); + } + ++static int pd692x0_pi_set_prio(struct pse_controller_dev *pcdev, int id, ++ unsigned int prio) ++{ ++ struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); ++ struct pd692x0_msg msg, buf = {0}; ++ int ret; ++ ++ ret = pd692x0_fw_unavailable(priv); ++ if (ret) ++ return ret; ++ ++ msg = pd692x0_msg_template_list[PD692X0_MSG_SET_PORT_PARAM]; ++ msg.sub[2] = id; ++ /* Controller priority from 1 to 3 */ ++ msg.data[4] = prio + 1; ++ ++ return pd692x0_sendrecv_msg(priv, &msg, &buf); ++} ++ + static const struct pse_controller_ops pd692x0_ops = { + .setup_pi_matrix = pd692x0_setup_pi_matrix, + .pi_get_admin_state = pd692x0_pi_get_admin_state, +@@ -1084,6 +1285,8 @@ static const struct pse_controller_ops p + .pi_get_pw_limit = pd692x0_pi_get_pw_limit, + .pi_set_pw_limit = pd692x0_pi_set_pw_limit, + .pi_get_pw_limit_ranges = pd692x0_pi_get_pw_limit_ranges, ++ .pi_get_prio = pd692x0_pi_get_prio, ++ .pi_set_prio = pd692x0_pi_set_prio, + }; + + #define PD692X0_FW_LINE_MAX_SZ 0xff +@@ -1500,6 +1703,8 @@ static int pd692x0_i2c_probe(struct i2c_ + priv->pcdev.ops = &pd692x0_ops; + priv->pcdev.dev = dev; + priv->pcdev.types = ETHTOOL_PSE_C33; ++ priv->pcdev.supp_budget_eval_strategies = PSE_BUDGET_EVAL_STRAT_DYNAMIC; ++ priv->pcdev.pis_prio_max = 2; + ret = devm_pse_controller_register(dev, &priv->pcdev); + if (ret) + return dev_err_probe(dev, ret, diff --git a/target/linux/generic/backport-6.12/626-18-v6.17-net-pse-pd-pd692x0-Add-controller-and-manager-power.patch b/target/linux/generic/backport-6.12/626-18-v6.17-net-pse-pd-pd692x0-Add-controller-and-manager-power.patch new file mode 100644 index 00000000000..a93dca09995 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-18-v6.17-net-pse-pd-pd692x0-Add-controller-and-manager-power.patch @@ -0,0 +1,71 @@ +From 24a4e3a05dd0eadd0c9585c411880e5dcb6be97f Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:09 +0200 +Subject: [PATCH] net: pse-pd: pd692x0: Add support for controller and manager + power supplies + +Add support for managing the VDD and VDDA power supplies for the PD692x0 +PSE controller, as well as the VAUX5 and VAUX3P3 power supplies for the +PD6920x PSE managers. + +Signed-off-by: Kory Maincent (Dent Project) +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-10-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pd692x0.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/drivers/net/pse-pd/pd692x0.c ++++ b/drivers/net/pse-pd/pd692x0.c +@@ -976,8 +976,10 @@ pd692x0_register_managers_regulator(stru + reg_name_len = strlen(dev_name(dev)) + 23; + + for (i = 0; i < nmanagers; i++) { ++ static const char * const regulators[] = { "vaux5", "vaux3p3" }; + struct regulator_dev *rdev; + char *reg_name; ++ int ret; + + reg_name = devm_kzalloc(dev, reg_name_len, GFP_KERNEL); + if (!reg_name) +@@ -988,6 +990,17 @@ pd692x0_register_managers_regulator(stru + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + ++ /* VMAIN is described as main supply for the manager. ++ * Add other VAUX power supplies and link them to the ++ * virtual device rdev->dev. ++ */ ++ ret = devm_regulator_bulk_get_enable(&rdev->dev, ++ ARRAY_SIZE(regulators), ++ regulators); ++ if (ret) ++ return dev_err_probe(&rdev->dev, ret, ++ "Failed to enable regulators\n"); ++ + priv->manager_reg[i] = rdev; + } + +@@ -1640,6 +1653,7 @@ static const struct fw_upload_ops pd692x + + static int pd692x0_i2c_probe(struct i2c_client *client) + { ++ static const char * const regulators[] = { "vdd", "vdda" }; + struct pd692x0_msg msg, buf = {0}, zero = {0}; + struct device *dev = &client->dev; + struct pd692x0_msg_ver ver; +@@ -1647,6 +1661,12 @@ static int pd692x0_i2c_probe(struct i2c_ + struct fw_upload *fwl; + int ret; + ++ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), ++ regulators); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to enable regulators\n"); ++ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(dev, "i2c check functionality failed\n"); + return -ENXIO; diff --git a/target/linux/generic/backport-6.12/626-19-v6.17-net-pse-pd-tps23881-Add-static-port-priority-feature.patch b/target/linux/generic/backport-6.12/626-19-v6.17-net-pse-pd-tps23881-Add-static-port-priority-feature.patch new file mode 100644 index 00000000000..739233a12e3 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-19-v6.17-net-pse-pd-tps23881-Add-static-port-priority-feature.patch @@ -0,0 +1,392 @@ +From 56cfc97635e9164395c9242f72746454347155ab Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:11 +0200 +Subject: [PATCH] net: pse-pd: tps23881: Add support for static port priority + feature + +This patch enhances PSE callbacks by introducing support for the static +port priority feature. It extends interrupt management to handle and report +detection, classification, and disconnection events. Additionally, it +introduces the pi_get_pw_req() callback, which provides information about +the power requested by the Powered Devices. + +Interrupt support is essential for the proper functioning of the TPS23881 +controller. Without it, after a power-on (PWON), the controller will +no longer perform detection and classification. This could lead to +potential hazards, such as connecting a non-PoE device after a PoE device, +which might result in magic smoke. + +Signed-off-by: Kory Maincent (Dent Project) +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-12-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/tps23881.c | 244 +++++++++++++++++++++++++++++++--- + 1 file changed, 228 insertions(+), 16 deletions(-) + +--- a/drivers/net/pse-pd/tps23881.c ++++ b/drivers/net/pse-pd/tps23881.c +@@ -20,20 +20,30 @@ + + #define TPS23881_REG_IT 0x0 + #define TPS23881_REG_IT_MASK 0x1 ++#define TPS23881_REG_IT_DISF BIT(2) ++#define TPS23881_REG_IT_DETC BIT(3) ++#define TPS23881_REG_IT_CLASC BIT(4) + #define TPS23881_REG_IT_IFAULT BIT(5) + #define TPS23881_REG_IT_SUPF BIT(7) ++#define TPS23881_REG_DET_EVENT 0x5 + #define TPS23881_REG_FAULT 0x7 + #define TPS23881_REG_SUPF_EVENT 0xb + #define TPS23881_REG_TSD BIT(7) ++#define TPS23881_REG_DISC 0xc + #define TPS23881_REG_PW_STATUS 0x10 + #define TPS23881_REG_OP_MODE 0x12 ++#define TPS23881_REG_DISC_EN 0x13 + #define TPS23881_OP_MODE_SEMIAUTO 0xaaaa + #define TPS23881_REG_DIS_EN 0x13 + #define TPS23881_REG_DET_CLA_EN 0x14 + #define TPS23881_REG_GEN_MASK 0x17 ++#define TPS23881_REG_CLCHE BIT(2) ++#define TPS23881_REG_DECHE BIT(3) + #define TPS23881_REG_NBITACC BIT(5) + #define TPS23881_REG_INTEN BIT(7) + #define TPS23881_REG_PW_EN 0x19 ++#define TPS23881_REG_RESET 0x1a ++#define TPS23881_REG_CLRAIN BIT(7) + #define TPS23881_REG_2PAIR_POL1 0x1e + #define TPS23881_REG_PORT_MAP 0x26 + #define TPS23881_REG_PORT_POWER 0x29 +@@ -178,6 +188,7 @@ static int tps23881_pi_enable(struct pse + struct i2c_client *client = priv->client; + u8 chan; + u16 val; ++ int ret; + + if (id >= TPS23881_MAX_CHANS) + return -ERANGE; +@@ -191,7 +202,22 @@ static int tps23881_pi_enable(struct pse + BIT(chan % 4)); + } + +- return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); ++ ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); ++ if (ret) ++ return ret; ++ ++ /* Enable DC disconnect*/ ++ chan = priv->port[id].chan[0]; ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_DISC_EN); ++ if (ret < 0) ++ return ret; ++ ++ val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4)); ++ ret = i2c_smbus_write_word_data(client, TPS23881_REG_DISC_EN, val); ++ if (ret) ++ return ret; ++ ++ return 0; + } + + static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id) +@@ -224,6 +250,17 @@ static int tps23881_pi_disable(struct ps + */ + mdelay(5); + ++ /* Disable DC disconnect*/ ++ chan = priv->port[id].chan[0]; ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_DISC_EN); ++ if (ret < 0) ++ return ret; ++ ++ val = tps23881_set_val(ret, chan, 0, 0, BIT(chan % 4)); ++ ret = i2c_smbus_write_word_data(client, TPS23881_REG_DISC_EN, val); ++ if (ret) ++ return ret; ++ + /* Enable detection and classification */ + ret = i2c_smbus_read_word_data(client, TPS23881_REG_DET_CLA_EN); + if (ret < 0) +@@ -919,6 +956,47 @@ static int tps23881_setup_pi_matrix(stru + return ret; + } + ++static int tps23881_power_class_table[] = { ++ -ERANGE, ++ 4000, ++ 7000, ++ 15500, ++ 30000, ++ 15500, ++ 15500, ++ -ERANGE, ++ 45000, ++ 60000, ++ 75000, ++ 90000, ++ 15500, ++ 45000, ++ -ERANGE, ++ -ERANGE, ++}; ++ ++static int tps23881_pi_get_pw_req(struct pse_controller_dev *pcdev, int id) ++{ ++ struct tps23881_priv *priv = to_tps23881_priv(pcdev); ++ struct i2c_client *client = priv->client; ++ u8 reg, chan; ++ int ret; ++ u16 val; ++ ++ /* For a 4-pair the classification need 5ms to be completed */ ++ if (priv->port[id].is_4p) ++ mdelay(5); ++ ++ chan = priv->port[id].chan[0]; ++ reg = TPS23881_REG_DISC + (chan % 4); ++ ret = i2c_smbus_read_word_data(client, reg); ++ if (ret < 0) ++ return ret; ++ ++ val = tps23881_calc_val(ret, chan, 4, 0xf); ++ return tps23881_power_class_table[val]; ++} ++ + static const struct pse_controller_ops tps23881_ops = { + .setup_pi_matrix = tps23881_setup_pi_matrix, + .pi_enable = tps23881_pi_enable, +@@ -931,6 +1009,7 @@ static const struct pse_controller_ops t + .pi_get_pw_limit = tps23881_pi_get_pw_limit, + .pi_set_pw_limit = tps23881_pi_set_pw_limit, + .pi_get_pw_limit_ranges = tps23881_pi_get_pw_limit_ranges, ++ .pi_get_pw_req = tps23881_pi_get_pw_req, + }; + + static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin"; +@@ -1088,17 +1167,113 @@ static void tps23881_irq_event_over_temp + } + } + +-static void tps23881_irq_event_over_current(struct tps23881_priv *priv, +- u16 reg_val, +- unsigned long *notifs, +- unsigned long *notifs_mask) ++static int tps23881_irq_event_over_current(struct tps23881_priv *priv, ++ u16 reg_val, ++ unsigned long *notifs, ++ unsigned long *notifs_mask) + { ++ int i, ret; + u8 chans; + + chans = tps23881_irq_export_chans_helper(reg_val, 0); ++ if (!chans) ++ return 0; ++ ++ tps23881_set_notifs_helper(priv, chans, notifs, notifs_mask, ++ ETHTOOL_PSE_EVENT_OVER_CURRENT | ++ ETHTOOL_C33_PSE_EVENT_DISCONNECTION); ++ ++ /* Over Current event resets the power limit registers so we need ++ * to configured it again. ++ */ ++ for_each_set_bit(i, notifs_mask, priv->pcdev.nr_lines) { ++ if (priv->port[i].pw_pol < 0) ++ continue; ++ ++ ret = tps23881_pi_enable_manual_pol(priv, i); ++ if (ret < 0) ++ return ret; ++ ++ /* Set power policy */ ++ ret = tps23881_pi_set_pw_pol_limit(priv, i, ++ priv->port[i].pw_pol, ++ priv->port[i].is_4p); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void tps23881_irq_event_disconnection(struct tps23881_priv *priv, ++ u16 reg_val, ++ unsigned long *notifs, ++ unsigned long *notifs_mask) ++{ ++ u8 chans; ++ ++ chans = tps23881_irq_export_chans_helper(reg_val, 4); + if (chans) + tps23881_set_notifs_helper(priv, chans, notifs, notifs_mask, +- ETHTOOL_PSE_EVENT_OVER_CURRENT); ++ ETHTOOL_C33_PSE_EVENT_DISCONNECTION); ++} ++ ++static int tps23881_irq_event_detection(struct tps23881_priv *priv, ++ u16 reg_val, ++ unsigned long *notifs, ++ unsigned long *notifs_mask) ++{ ++ enum ethtool_pse_event event; ++ int reg, ret, i, val; ++ unsigned long chans; ++ ++ chans = tps23881_irq_export_chans_helper(reg_val, 0); ++ for_each_set_bit(i, &chans, TPS23881_MAX_CHANS) { ++ reg = TPS23881_REG_DISC + (i % 4); ++ ret = i2c_smbus_read_word_data(priv->client, reg); ++ if (ret < 0) ++ return ret; ++ ++ val = tps23881_calc_val(ret, i, 0, 0xf); ++ /* If detection valid */ ++ if (val == 0x4) ++ event = ETHTOOL_C33_PSE_EVENT_DETECTION; ++ else ++ event = ETHTOOL_C33_PSE_EVENT_DISCONNECTION; ++ ++ tps23881_set_notifs_helper(priv, BIT(i), notifs, ++ notifs_mask, event); ++ } ++ ++ return 0; ++} ++ ++static int tps23881_irq_event_classification(struct tps23881_priv *priv, ++ u16 reg_val, ++ unsigned long *notifs, ++ unsigned long *notifs_mask) ++{ ++ int reg, ret, val, i; ++ unsigned long chans; ++ ++ chans = tps23881_irq_export_chans_helper(reg_val, 4); ++ for_each_set_bit(i, &chans, TPS23881_MAX_CHANS) { ++ reg = TPS23881_REG_DISC + (i % 4); ++ ret = i2c_smbus_read_word_data(priv->client, reg); ++ if (ret < 0) ++ return ret; ++ ++ val = tps23881_calc_val(ret, i, 4, 0xf); ++ /* Do not report classification event for unknown class */ ++ if (!val || val == 0x8 || val == 0xf) ++ continue; ++ ++ tps23881_set_notifs_helper(priv, BIT(i), notifs, ++ notifs_mask, ++ ETHTOOL_C33_PSE_EVENT_CLASSIFICATION); ++ } ++ ++ return 0; + } + + static int tps23881_irq_event_handler(struct tps23881_priv *priv, u16 reg, +@@ -1106,7 +1281,7 @@ static int tps23881_irq_event_handler(st + unsigned long *notifs_mask) + { + struct i2c_client *client = priv->client; +- int ret; ++ int ret, val; + + /* The Supply event bit is repeated twice so we only need to read + * the one from the first byte. +@@ -1118,13 +1293,36 @@ static int tps23881_irq_event_handler(st + tps23881_irq_event_over_temp(priv, ret, notifs, notifs_mask); + } + +- if (reg & (TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_IFAULT << 8)) { ++ if (reg & (TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_IFAULT << 8 | ++ TPS23881_REG_IT_DISF | TPS23881_REG_IT_DISF << 8)) { + ret = i2c_smbus_read_word_data(client, TPS23881_REG_FAULT); + if (ret < 0) + return ret; +- tps23881_irq_event_over_current(priv, ret, notifs, notifs_mask); ++ ret = tps23881_irq_event_over_current(priv, ret, notifs, ++ notifs_mask); ++ if (ret) ++ return ret; ++ ++ tps23881_irq_event_disconnection(priv, ret, notifs, notifs_mask); + } + ++ if (reg & (TPS23881_REG_IT_DETC | TPS23881_REG_IT_DETC << 8 | ++ TPS23881_REG_IT_CLASC | TPS23881_REG_IT_CLASC << 8)) { ++ ret = i2c_smbus_read_word_data(client, TPS23881_REG_DET_EVENT); ++ if (ret < 0) ++ return ret; ++ ++ val = ret; ++ ret = tps23881_irq_event_detection(priv, val, notifs, ++ notifs_mask); ++ if (ret) ++ return ret; ++ ++ ret = tps23881_irq_event_classification(priv, val, notifs, ++ notifs_mask); ++ if (ret) ++ return ret; ++ } + return 0; + } + +@@ -1178,7 +1376,14 @@ static int tps23881_setup_irq(struct tps + int ret; + u16 val; + +- val = TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_SUPF; ++ if (!irq) { ++ dev_err(&client->dev, "interrupt is missing"); ++ return -EINVAL; ++ } ++ ++ val = TPS23881_REG_IT_IFAULT | TPS23881_REG_IT_SUPF | ++ TPS23881_REG_IT_DETC | TPS23881_REG_IT_CLASC | ++ TPS23881_REG_IT_DISF; + val |= val << 8; + ret = i2c_smbus_write_word_data(client, TPS23881_REG_IT_MASK, val); + if (ret) +@@ -1188,11 +1393,19 @@ static int tps23881_setup_irq(struct tps + if (ret < 0) + return ret; + +- val = (u16)(ret | TPS23881_REG_INTEN | TPS23881_REG_INTEN << 8); ++ val = TPS23881_REG_INTEN | TPS23881_REG_CLCHE | TPS23881_REG_DECHE; ++ val |= val << 8; ++ val |= (u16)ret; + ret = i2c_smbus_write_word_data(client, TPS23881_REG_GEN_MASK, val); + if (ret < 0) + return ret; + ++ /* Reset interrupts registers */ ++ ret = i2c_smbus_write_word_data(client, TPS23881_REG_RESET, ++ TPS23881_REG_CLRAIN); ++ if (ret < 0) ++ return ret; ++ + return devm_pse_irq_helper(&priv->pcdev, irq, 0, &irq_desc); + } + +@@ -1270,17 +1483,16 @@ static int tps23881_i2c_probe(struct i2c + priv->pcdev.dev = dev; + priv->pcdev.types = ETHTOOL_PSE_C33; + priv->pcdev.nr_lines = TPS23881_MAX_CHANS; ++ priv->pcdev.supp_budget_eval_strategies = PSE_BUDGET_EVAL_STRAT_STATIC; + ret = devm_pse_controller_register(dev, &priv->pcdev); + if (ret) { + return dev_err_probe(dev, ret, + "failed to register PSE controller\n"); + } + +- if (client->irq) { +- ret = tps23881_setup_irq(priv, client->irq); +- if (ret) +- return ret; +- } ++ ret = tps23881_setup_irq(priv, client->irq); ++ if (ret) ++ return ret; + + return ret; + } diff --git a/target/linux/generic/backport-6.12/626-20-v6.17-net-pse-pd-pd692x0-reduce-stack-usage.patch b/target/linux/generic/backport-6.12/626-20-v6.17-net-pse-pd-pd692x0-reduce-stack-usage.patch new file mode 100644 index 00000000000..d5e1aaf9a32 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-20-v6.17-net-pse-pd-pd692x0-reduce-stack-usage.patch @@ -0,0 +1,55 @@ +From d12b3dc106090b358fb67b7c0c717a0884327ddf Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Wed, 9 Jul 2025 17:32:04 +0200 +Subject: [PATCH] net: pse-pd: pd692x0: reduce stack usage in + pd692x0_setup_pi_matrix + +The pd692x0_manager array in this function is really too big to fit on the +stack, though this never triggered a warning until a recent patch made +it slightly bigger: + +drivers/net/pse-pd/pd692x0.c: In function 'pd692x0_setup_pi_matrix': +drivers/net/pse-pd/pd692x0.c:1210:1: error: the frame size of 1584 bytes is larger than 1536 bytes [-Werror=frame-larger-than=] + +Change the function to dynamically allocate the array here. + +Fixes: 359754013e6a ("net: pse-pd: pd692x0: Add support for PSE PI priority feature") +Signed-off-by: Arnd Bergmann +Reviewed-by: Kory Maincent +Link: https://patch.msgid.link/20250709153210.1920125-1-arnd@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pd692x0.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) +--- a/drivers/net/pse-pd/pd692x0.c ++++ b/drivers/net/pse-pd/pd692x0.c +@@ -860,7 +860,7 @@ out: + + static int + pd692x0_of_get_managers(struct pd692x0_priv *priv, +- struct pd692x0_manager manager[PD692X0_MAX_MANAGERS]) ++ struct pd692x0_manager *manager) + { + struct device_node *managers_node, *node; + int ret, nmanagers, i, j; +@@ -1164,7 +1164,7 @@ pd692x0_write_ports_matrix(struct pd692x + + static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev) + { +- struct pd692x0_manager manager[PD692X0_MAX_MANAGERS] = {0}; ++ struct pd692x0_manager *manager __free(kfree) = NULL; + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); + struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS]; + int ret, i, j, nmanagers; +@@ -1174,6 +1174,10 @@ static int pd692x0_setup_pi_matrix(struc + priv->fw_state != PD692X0_FW_COMPLETE) + return 0; + ++ manager = kcalloc(PD692X0_MAX_MANAGERS, sizeof(*manager), GFP_KERNEL); ++ if (!manager) ++ return -ENOMEM; ++ + ret = pd692x0_of_get_managers(priv, manager); + if (ret < 0) + return ret; diff --git a/target/linux/generic/backport-6.12/626-21-v6.18-net-pse-pd-pd692x0-Fix-power-budget-leak.patch b/target/linux/generic/backport-6.12/626-21-v6.18-net-pse-pd-pd692x0-Fix-power-budget-leak.patch new file mode 100644 index 00000000000..af237459470 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-21-v6.18-net-pse-pd-pd692x0-Fix-power-budget-leak.patch @@ -0,0 +1,121 @@ +From 1c67f9c54cdc70627e3f6472b89cd3d895df974c Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Wed, 20 Aug 2025 15:27:07 +0200 +Subject: [PATCH] net: pse-pd: pd692x0: Fix power budget leak in manager setup + error path + +Fix a resource leak where manager power budgets were freed on both +success and error paths during manager setup. Power budgets should +only be freed on error paths after regulator registration or during +driver removal. + +Refactor cleanup logic by extracting OF node cleanup and power budget +freeing into separate helper functions for better maintainability. + +Fixes: 359754013e6a ("net: pse-pd: pd692x0: Add support for PSE PI priority feature") +Signed-off-by: Kory Maincent +Link: https://patch.msgid.link/20250820132708.837255-1-kory.maincent@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pd692x0.c | 59 +++++++++++++++++++++++++++--------- + 1 file changed, 44 insertions(+), 15 deletions(-) + +--- a/drivers/net/pse-pd/pd692x0.c ++++ b/drivers/net/pse-pd/pd692x0.c +@@ -1162,12 +1162,44 @@ pd692x0_write_ports_matrix(struct pd692x + return 0; + } + ++static void pd692x0_of_put_managers(struct pd692x0_priv *priv, ++ struct pd692x0_manager *manager, ++ int nmanagers) ++{ ++ int i, j; ++ ++ for (i = 0; i < nmanagers; i++) { ++ for (j = 0; j < manager[i].nports; j++) ++ of_node_put(manager[i].port_node[j]); ++ of_node_put(manager[i].node); ++ } ++} ++ ++static void pd692x0_managers_free_pw_budget(struct pd692x0_priv *priv) ++{ ++ int i; ++ ++ for (i = 0; i < PD692X0_MAX_MANAGERS; i++) { ++ struct regulator *supply; ++ ++ if (!priv->manager_reg[i] || !priv->manager_pw_budget[i]) ++ continue; ++ ++ supply = priv->manager_reg[i]->supply; ++ if (!supply) ++ continue; ++ ++ regulator_free_power_budget(supply, ++ priv->manager_pw_budget[i]); ++ } ++} ++ + static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev) + { + struct pd692x0_manager *manager __free(kfree) = NULL; + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); + struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS]; +- int ret, i, j, nmanagers; ++ int ret, nmanagers; + + /* Should we flash the port matrix */ + if (priv->fw_state != PD692X0_FW_OK && +@@ -1185,31 +1217,27 @@ static int pd692x0_setup_pi_matrix(struc + nmanagers = ret; + ret = pd692x0_register_managers_regulator(priv, manager, nmanagers); + if (ret) +- goto out; ++ goto err_of_managers; + + ret = pd692x0_configure_managers(priv, nmanagers); + if (ret) +- goto out; ++ goto err_of_managers; + + ret = pd692x0_set_ports_matrix(priv, manager, nmanagers, port_matrix); + if (ret) +- goto out; ++ goto err_managers_req_pw; + + ret = pd692x0_write_ports_matrix(priv, port_matrix); + if (ret) +- goto out; +- +-out: +- for (i = 0; i < nmanagers; i++) { +- struct regulator *supply = priv->manager_reg[i]->supply; ++ goto err_managers_req_pw; + +- regulator_free_power_budget(supply, +- priv->manager_pw_budget[i]); ++ pd692x0_of_put_managers(priv, manager, nmanagers); ++ return 0; + +- for (j = 0; j < manager[i].nports; j++) +- of_node_put(manager[i].port_node[j]); +- of_node_put(manager[i].node); +- } ++err_managers_req_pw: ++ pd692x0_managers_free_pw_budget(priv); ++err_of_managers: ++ pd692x0_of_put_managers(priv, manager, nmanagers); + return ret; + } + +@@ -1748,6 +1776,7 @@ static void pd692x0_i2c_remove(struct i2 + { + struct pd692x0_priv *priv = i2c_get_clientdata(client); + ++ pd692x0_managers_free_pw_budget(priv); + firmware_upload_unregister(priv->fwl); + } + diff --git a/target/linux/generic/backport-6.12/626-22-v6.18-net-pse-pd-pd692x0-Skip-power-budget-when-undefined.patch b/target/linux/generic/backport-6.12/626-22-v6.18-net-pse-pd-pd692x0-Skip-power-budget-when-undefined.patch new file mode 100644 index 00000000000..98da7b5080e --- /dev/null +++ b/target/linux/generic/backport-6.12/626-22-v6.18-net-pse-pd-pd692x0-Skip-power-budget-when-undefined.patch @@ -0,0 +1,37 @@ +From 7ef353879f714602b43f98662069f4fb86536761 Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Wed, 20 Aug 2025 15:33:21 +0200 +Subject: [PATCH] net: pse-pd: pd692x0: Skip power budget configuration when + undefined + +If the power supply's power budget is not defined in the device tree, +the current code still requests power and configures the PSE manager +with a 0W power limit, which is undesirable behavior. + +Skip power budget configuration entirely when the budget is zero, +avoiding unnecessary power requests and preventing invalid 0W limits +from being set on the PSE manager. + +Fixes: 359754013e6a ("net: pse-pd: pd692x0: Add support for PSE PI priority feature") +Signed-off-by: Kory Maincent +Acked-by: Oleksij Rempel +Link: https://patch.msgid.link/20250820133321.841054-1-kory.maincent@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pd692x0.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/pse-pd/pd692x0.c ++++ b/drivers/net/pse-pd/pd692x0.c +@@ -1041,6 +1041,10 @@ pd692x0_configure_managers(struct pd692x + int pw_budget; + + pw_budget = regulator_get_unclaimed_power_budget(supply); ++ if (!pw_budget) ++ /* Do nothing if no power budget */ ++ continue; ++ + /* Max power budget per manager */ + if (pw_budget > 6000000) + pw_budget = 6000000; diff --git a/target/linux/generic/backport-6.12/626-23-v6.18-net-pse-pd-Add-Si3474-PSE-controller-driver.patch b/target/linux/generic/backport-6.12/626-23-v6.18-net-pse-pd-Add-Si3474-PSE-controller-driver.patch new file mode 100644 index 00000000000..2f4087facca --- /dev/null +++ b/target/linux/generic/backport-6.12/626-23-v6.18-net-pse-pd-Add-Si3474-PSE-controller-driver.patch @@ -0,0 +1,636 @@ +From a2317231df4b22e6634fe3d8645e7cef848acf49 Mon Sep 17 00:00:00 2001 +From: Piotr Kubik +Date: Tue, 26 Aug 2025 14:41:58 +0000 +Subject: [PATCH] net: pse-pd: Add Si3474 PSE controller driver + +Add a driver for the Skyworks Si3474 I2C Power Sourcing Equipment +controller. + +Driver supports basic features of Si3474 IC: +- get port status, +- get port power, +- get port voltage, +- enable/disable port power. + +Only 4p configurations are supported at this moment. + +Signed-off-by: Piotr Kubik +Reviewed-by: Kory Maincent +Link: https://patch.msgid.link/9b72c8cd-c8d3-4053-9c80-671b9481d166@adtran.com +Signed-off-by: Paolo Abeni +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/Kconfig | 11 + + drivers/net/pse-pd/Makefile | 1 + + drivers/net/pse-pd/si3474.c | 578 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 590 insertions(+) + create mode 100644 drivers/net/pse-pd/si3474.c +--- a/drivers/net/pse-pd/Kconfig ++++ b/drivers/net/pse-pd/Kconfig +@@ -32,6 +32,17 @@ config PSE_PD692X0 + To compile this driver as a module, choose M here: the + module will be called pd692x0. + ++config PSE_SI3474 ++ tristate "Si3474 PSE controller" ++ depends on I2C ++ help ++ This module provides support for Si3474 regulator based Ethernet ++ Power Sourcing Equipment. ++ Only 4-pair PSE configurations are supported. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called si3474. ++ + config PSE_TPS23881 + tristate "TPS23881 PSE controller" + depends on I2C +--- a/drivers/net/pse-pd/Makefile ++++ b/drivers/net/pse-pd/Makefile +@@ -5,4 +5,5 @@ obj-$(CONFIG_PSE_CONTROLLER) += pse_core + + obj-$(CONFIG_PSE_REGULATOR) += pse_regulator.o + obj-$(CONFIG_PSE_PD692X0) += pd692x0.o ++obj-$(CONFIG_PSE_SI3474) += si3474.o + obj-$(CONFIG_PSE_TPS23881) += tps23881.o +--- /dev/null ++++ b/drivers/net/pse-pd/si3474.c +@@ -0,0 +1,578 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Driver for the Skyworks Si3474 PoE PSE Controller ++ * ++ * Chip Architecture & Terminology: ++ * ++ * The Si3474 is a single-chip PoE PSE controller managing 8 physical power ++ * delivery channels. Internally, it's structured into two logical "Quads". ++ * ++ * Quad 0: Manages physical channels ('ports' in datasheet) 0, 1, 2, 3 ++ * Quad 1: Manages physical channels ('ports' in datasheet) 4, 5, 6, 7 ++ * ++ * Each Quad is accessed via a separate I2C address. The base address range is ++ * set by hardware pins A1-A4, and the specific address selects Quad 0 (usually ++ * the lower/even address) or Quad 1 (usually the higher/odd address). ++ * See datasheet Table 2.2 for the address mapping. ++ * ++ * While the Quads manage channel-specific operations, the Si3474 package has ++ * several resources shared across the entire chip: ++ * - Single RESETb input pin. ++ * - Single INTb output pin (signals interrupts from *either* Quad). ++ * - Single OSS input pin (Emergency Shutdown). ++ * - Global I2C Address (0x7F) used for firmware updates. ++ * - Global status monitoring (Temperature, VDD/VPWR Undervoltage Lockout). ++ * ++ * Driver Architecture: ++ * ++ * To handle the mix of per-Quad access and shared resources correctly, this ++ * driver treats the entire Si3474 package as one logical device. The driver ++ * instance associated with the primary I2C address (Quad 0) takes ownership. ++ * It discovers and manages the I2C client for the secondary address (Quad 1). ++ * This primary instance handles shared resources like IRQ management and ++ * registers a single PSE controller device representing all logical PIs. ++ * Internal functions route I2C commands to the appropriate Quad's i2c_client ++ * based on the target channel or PI. ++ * ++ * Terminology Mapping: ++ * ++ * - "PI" (Power Interface): Refers to the logical PSE port as defined by ++ * IEEE 802.3 (typically corresponds to an RJ45 connector). This is the ++ * `id` (0-7) used in the pse_controller_ops. ++ * - "Channel": Refers to one of the 8 physical power control paths within ++ * the Si3474 chip itself (hardware channels 0-7). This terminology is ++ * used internally within the driver to avoid confusion with 'ports'. ++ * - "Quad": One of the two internal 4-channel management units within the ++ * Si3474, each accessed via its own I2C address. ++ * ++ * Relationship: ++ * - A 2-Pair PoE PI uses 1 Channel. ++ * - A 4-Pair PoE PI uses 2 Channels. ++ * ++ * ASCII Schematic: ++ * ++ * +-----------------------------------------------------+ ++ * | Si3474 Chip | ++ * | | ++ * | +---------------------+ +---------------------+ | ++ * | | Quad 0 | | Quad 1 | | ++ * | | Channels 0, 1, 2, 3 | | Channels 4, 5, 6, 7 | | ++ * | +----------^----------+ +-------^-------------+ | ++ * | I2C Addr 0 | | I2C Addr 1 | ++ * | +------------------------+ | ++ * | (Primary Driver Instance) (Managed by Primary) | ++ * | | ++ * | Shared Resources (affect whole chip): | ++ * | - Single INTb Output -> Handled by Primary | ++ * | - Single RESETb Input | ++ * | - Single OSS Input -> Handled by Primary | ++ * | - Global I2C Addr (0x7F) for Firmware Update | ++ * | - Global Status (Temp, VDD/VPWR UVLO) | ++ * +-----------------------------------------------------+ ++ * | | | | | | | | ++ * Ch0 Ch1 Ch2 Ch3 Ch4 Ch5 Ch6 Ch7 (Physical Channels) ++ * ++ * Example Mapping (Logical PI to Physical Channel(s)): ++ * * 2-Pair Mode (8 PIs): ++ * PI 0 -> Ch 0 ++ * PI 1 -> Ch 1 ++ * ... ++ * PI 7 -> Ch 7 ++ * * 4-Pair Mode (4 PIs): ++ * PI 0 -> Ch 0 + Ch 1 (Managed via Quad 0 Addr) ++ * PI 1 -> Ch 2 + Ch 3 (Managed via Quad 0 Addr) ++ * PI 2 -> Ch 4 + Ch 5 (Managed via Quad 1 Addr) ++ * PI 3 -> Ch 6 + Ch 7 (Managed via Quad 1 Addr) ++ * (Note: Actual mapping depends on Device Tree and PORT_REMAP config) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define SI3474_MAX_CHANS 8 ++ ++#define MANUFACTURER_ID 0x08 ++#define IC_ID 0x05 ++#define SI3474_DEVICE_ID (MANUFACTURER_ID << 3 | IC_ID) ++ ++/* Misc registers */ ++#define VENDOR_IC_ID_REG 0x1B ++#define TEMPERATURE_REG 0x2C ++#define FIRMWARE_REVISION_REG 0x41 ++#define CHIP_REVISION_REG 0x43 ++ ++/* Main status registers */ ++#define POWER_STATUS_REG 0x10 ++#define PORT_MODE_REG 0x12 ++#define DETECT_CLASS_ENABLE_REG 0x14 ++ ++/* PORTn Current */ ++#define PORT1_CURRENT_LSB_REG 0x30 ++ ++/* PORTn Current [mA], return in [nA] */ ++/* 1000 * ((PORTn_CURRENT_MSB << 8) + PORTn_CURRENT_LSB) / 16384 */ ++#define SI3474_NA_STEP (1000 * 1000 * 1000 / 16384) ++ ++/* VPWR Voltage */ ++#define VPWR_LSB_REG 0x2E ++#define VPWR_MSB_REG 0x2F ++ ++/* PORTn Voltage */ ++#define PORT1_VOLTAGE_LSB_REG 0x32 ++ ++/* VPWR Voltage [V], return in [uV] */ ++/* 60 * (( VPWR_MSB << 8) + VPWR_LSB) / 16384 */ ++#define SI3474_UV_STEP (1000 * 1000 * 60 / 16384) ++ ++/* Helper macros */ ++#define CHAN_IDX(chan) ((chan) % 4) ++#define CHAN_BIT(chan) BIT(CHAN_IDX(chan)) ++#define CHAN_UPPER_BIT(chan) BIT(CHAN_IDX(chan) + 4) ++ ++#define CHAN_MASK(chan) (0x03U << (2 * CHAN_IDX(chan))) ++#define CHAN_REG(base, chan) ((base) + (CHAN_IDX(chan) * 4)) ++ ++struct si3474_pi_desc { ++ u8 chan[2]; ++ bool is_4p; ++}; ++ ++struct si3474_priv { ++ struct i2c_client *client[2]; ++ struct pse_controller_dev pcdev; ++ struct device_node *np; ++ struct si3474_pi_desc pi[SI3474_MAX_CHANS]; ++}; ++ ++static struct si3474_priv *to_si3474_priv(struct pse_controller_dev *pcdev) ++{ ++ return container_of(pcdev, struct si3474_priv, pcdev); ++} ++ ++static void si3474_get_channels(struct si3474_priv *priv, int id, ++ u8 *chan0, u8 *chan1) ++{ ++ *chan0 = priv->pi[id].chan[0]; ++ *chan1 = priv->pi[id].chan[1]; ++} ++ ++static struct i2c_client *si3474_get_chan_client(struct si3474_priv *priv, ++ u8 chan) ++{ ++ return (chan < 4) ? priv->client[0] : priv->client[1]; ++} ++ ++static int si3474_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, ++ struct pse_admin_state *admin_state) ++{ ++ struct si3474_priv *priv = to_si3474_priv(pcdev); ++ struct i2c_client *client; ++ bool is_enabled; ++ u8 chan0, chan1; ++ s32 ret; ++ ++ si3474_get_channels(priv, id, &chan0, &chan1); ++ client = si3474_get_chan_client(priv, chan0); ++ ++ ret = i2c_smbus_read_byte_data(client, PORT_MODE_REG); ++ if (ret < 0) { ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN; ++ return ret; ++ } ++ ++ is_enabled = ret & (CHAN_MASK(chan0) | CHAN_MASK(chan1)); ++ ++ if (is_enabled) ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; ++ else ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; ++ ++ return 0; ++} ++ ++static int si3474_pi_get_pw_status(struct pse_controller_dev *pcdev, int id, ++ struct pse_pw_status *pw_status) ++{ ++ struct si3474_priv *priv = to_si3474_priv(pcdev); ++ struct i2c_client *client; ++ bool delivering; ++ u8 chan0, chan1; ++ s32 ret; ++ ++ si3474_get_channels(priv, id, &chan0, &chan1); ++ client = si3474_get_chan_client(priv, chan0); ++ ++ ret = i2c_smbus_read_byte_data(client, POWER_STATUS_REG); ++ if (ret < 0) { ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN; ++ return ret; ++ } ++ ++ delivering = ret & (CHAN_UPPER_BIT(chan0) | CHAN_UPPER_BIT(chan1)); ++ ++ if (delivering) ++ pw_status->c33_pw_status = ++ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; ++ else ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; ++ ++ return 0; ++} ++ ++static int si3474_get_of_channels(struct si3474_priv *priv) ++{ ++ struct pse_pi *pi; ++ u32 chan_id; ++ u8 pi_no; ++ s32 ret; ++ ++ for (pi_no = 0; pi_no < SI3474_MAX_CHANS; pi_no++) { ++ pi = &priv->pcdev.pi[pi_no]; ++ bool pairset_found = false; ++ u8 pairset_no; ++ ++ for (pairset_no = 0; pairset_no < 2; pairset_no++) { ++ if (!pi->pairset[pairset_no].np) ++ continue; ++ ++ pairset_found = true; ++ ++ ret = of_property_read_u32(pi->pairset[pairset_no].np, ++ "reg", &chan_id); ++ if (ret) { ++ dev_err(&priv->client[0]->dev, ++ "Failed to read channel reg property\n"); ++ return ret; ++ } ++ if (chan_id > SI3474_MAX_CHANS) { ++ dev_err(&priv->client[0]->dev, ++ "Incorrect channel number: %d\n", chan_id); ++ return -EINVAL; ++ } ++ ++ priv->pi[pi_no].chan[pairset_no] = chan_id; ++ /* Mark as 4-pair if second pairset is present */ ++ priv->pi[pi_no].is_4p = (pairset_no == 1); ++ } ++ ++ if (pairset_found && !priv->pi[pi_no].is_4p) { ++ dev_err(&priv->client[0]->dev, ++ "Second pairset is missing for PI %pOF, only 4p configs are supported\n", ++ pi->np); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int si3474_setup_pi_matrix(struct pse_controller_dev *pcdev) ++{ ++ struct si3474_priv *priv = to_si3474_priv(pcdev); ++ s32 ret; ++ ++ ret = si3474_get_of_channels(priv); ++ if (ret < 0) ++ dev_warn(&priv->client[0]->dev, ++ "Unable to parse DT PSE power interface matrix\n"); ++ ++ return ret; ++} ++ ++static int si3474_pi_enable(struct pse_controller_dev *pcdev, int id) ++{ ++ struct si3474_priv *priv = to_si3474_priv(pcdev); ++ struct i2c_client *client; ++ u8 chan0, chan1; ++ s32 ret; ++ u8 val; ++ ++ si3474_get_channels(priv, id, &chan0, &chan1); ++ client = si3474_get_chan_client(priv, chan0); ++ ++ /* Release PI from shutdown */ ++ ret = i2c_smbus_read_byte_data(client, PORT_MODE_REG); ++ if (ret < 0) ++ return ret; ++ ++ val = (u8)ret; ++ val |= CHAN_MASK(chan0); ++ val |= CHAN_MASK(chan1); ++ ++ ret = i2c_smbus_write_byte_data(client, PORT_MODE_REG, val); ++ if (ret) ++ return ret; ++ ++ /* DETECT_CLASS_ENABLE must be set when using AUTO mode, ++ * otherwise PI does not power up - datasheet section 2.10.2 ++ */ ++ val = CHAN_BIT(chan0) | CHAN_UPPER_BIT(chan0) | ++ CHAN_BIT(chan1) | CHAN_UPPER_BIT(chan1); ++ ++ ret = i2c_smbus_write_byte_data(client, DETECT_CLASS_ENABLE_REG, val); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int si3474_pi_disable(struct pse_controller_dev *pcdev, int id) ++{ ++ struct si3474_priv *priv = to_si3474_priv(pcdev); ++ struct i2c_client *client; ++ u8 chan0, chan1; ++ s32 ret; ++ u8 val; ++ ++ si3474_get_channels(priv, id, &chan0, &chan1); ++ client = si3474_get_chan_client(priv, chan0); ++ ++ /* Set PI in shutdown mode */ ++ ret = i2c_smbus_read_byte_data(client, PORT_MODE_REG); ++ if (ret < 0) ++ return ret; ++ ++ val = (u8)ret; ++ val &= ~CHAN_MASK(chan0); ++ val &= ~CHAN_MASK(chan1); ++ ++ ret = i2c_smbus_write_byte_data(client, PORT_MODE_REG, val); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int si3474_pi_get_chan_current(struct si3474_priv *priv, u8 chan) ++{ ++ struct i2c_client *client; ++ u64 tmp_64; ++ s32 ret; ++ u8 reg; ++ ++ client = si3474_get_chan_client(priv, chan); ++ ++ /* Registers 0x30 to 0x3d */ ++ reg = CHAN_REG(PORT1_CURRENT_LSB_REG, chan); ++ ++ ret = i2c_smbus_read_word_data(client, reg); ++ if (ret < 0) ++ return ret; ++ ++ tmp_64 = ret * SI3474_NA_STEP; ++ ++ /* uA = nA / 1000 */ ++ tmp_64 = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000); ++ return (int)tmp_64; ++} ++ ++static int si3474_pi_get_chan_voltage(struct si3474_priv *priv, u8 chan) ++{ ++ struct i2c_client *client; ++ s32 ret; ++ u32 val; ++ u8 reg; ++ ++ client = si3474_get_chan_client(priv, chan); ++ ++ /* Registers 0x32 to 0x3f */ ++ reg = CHAN_REG(PORT1_VOLTAGE_LSB_REG, chan); ++ ++ ret = i2c_smbus_read_word_data(client, reg); ++ if (ret < 0) ++ return ret; ++ ++ val = ret * SI3474_UV_STEP; ++ ++ return (int)val; ++} ++ ++static int si3474_pi_get_voltage(struct pse_controller_dev *pcdev, int id) ++{ ++ struct si3474_priv *priv = to_si3474_priv(pcdev); ++ struct i2c_client *client; ++ u8 chan0, chan1; ++ s32 ret; ++ ++ si3474_get_channels(priv, id, &chan0, &chan1); ++ client = si3474_get_chan_client(priv, chan0); ++ ++ /* Check which channels are enabled*/ ++ ret = i2c_smbus_read_byte_data(client, POWER_STATUS_REG); ++ if (ret < 0) ++ return ret; ++ ++ /* Take voltage from the first enabled channel */ ++ if (ret & CHAN_BIT(chan0)) ++ ret = si3474_pi_get_chan_voltage(priv, chan0); ++ else if (ret & CHAN_BIT(chan1)) ++ ret = si3474_pi_get_chan_voltage(priv, chan1); ++ else ++ /* 'should' be no voltage in this case */ ++ return 0; ++ ++ return ret; ++} ++ ++static int si3474_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id) ++{ ++ struct si3474_priv *priv = to_si3474_priv(pcdev); ++ u8 chan0, chan1; ++ u32 uV, uA; ++ u64 tmp_64; ++ s32 ret; ++ ++ ret = si3474_pi_get_voltage(&priv->pcdev, id); ++ ++ /* Do not read currents if voltage is 0 */ ++ if (ret <= 0) ++ return ret; ++ uV = ret; ++ ++ si3474_get_channels(priv, id, &chan0, &chan1); ++ ++ ret = si3474_pi_get_chan_current(priv, chan0); ++ if (ret < 0) ++ return ret; ++ uA = ret; ++ ++ ret = si3474_pi_get_chan_current(priv, chan1); ++ if (ret < 0) ++ return ret; ++ uA += ret; ++ ++ tmp_64 = uV; ++ tmp_64 *= uA; ++ /* mW = uV * uA / 1000000000 */ ++ return DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000); ++} ++ ++static const struct pse_controller_ops si3474_ops = { ++ .setup_pi_matrix = si3474_setup_pi_matrix, ++ .pi_enable = si3474_pi_enable, ++ .pi_disable = si3474_pi_disable, ++ .pi_get_actual_pw = si3474_pi_get_actual_pw, ++ .pi_get_voltage = si3474_pi_get_voltage, ++ .pi_get_admin_state = si3474_pi_get_admin_state, ++ .pi_get_pw_status = si3474_pi_get_pw_status, ++}; ++ ++static void si3474_ancillary_i2c_remove(void *data) ++{ ++ struct i2c_client *client = data; ++ ++ i2c_unregister_device(client); ++} ++ ++static int si3474_i2c_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct si3474_priv *priv; ++ u8 fw_version; ++ s32 ret; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ dev_err(dev, "i2c check functionality failed\n"); ++ return -ENXIO; ++ } ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ ret = i2c_smbus_read_byte_data(client, VENDOR_IC_ID_REG); ++ if (ret < 0) ++ return ret; ++ ++ if (ret != SI3474_DEVICE_ID) { ++ dev_err(dev, "Wrong device ID: 0x%x\n", ret); ++ return -ENXIO; ++ } ++ ++ ret = i2c_smbus_read_byte_data(client, FIRMWARE_REVISION_REG); ++ if (ret < 0) ++ return ret; ++ fw_version = ret; ++ ++ ret = i2c_smbus_read_byte_data(client, CHIP_REVISION_REG); ++ if (ret < 0) ++ return ret; ++ ++ dev_dbg(dev, "Chip revision: 0x%x, firmware version: 0x%x\n", ++ ret, fw_version); ++ ++ priv->client[0] = client; ++ i2c_set_clientdata(client, priv); ++ ++ priv->client[1] = i2c_new_ancillary_device(priv->client[0], "secondary", ++ priv->client[0]->addr + 1); ++ if (IS_ERR(priv->client[1])) ++ return PTR_ERR(priv->client[1]); ++ ++ ret = devm_add_action_or_reset(dev, si3474_ancillary_i2c_remove, priv->client[1]); ++ if (ret < 0) { ++ dev_err(&priv->client[1]->dev, "Cannot register remove callback\n"); ++ return ret; ++ } ++ ++ ret = i2c_smbus_read_byte_data(priv->client[1], VENDOR_IC_ID_REG); ++ if (ret < 0) { ++ dev_err(&priv->client[1]->dev, "Cannot access secondary PSE controller\n"); ++ return ret; ++ } ++ ++ if (ret != SI3474_DEVICE_ID) { ++ dev_err(&priv->client[1]->dev, ++ "Wrong device ID for secondary PSE controller: 0x%x\n", ret); ++ return -ENXIO; ++ } ++ ++ priv->np = dev->of_node; ++ priv->pcdev.owner = THIS_MODULE; ++ priv->pcdev.ops = &si3474_ops; ++ priv->pcdev.dev = dev; ++ priv->pcdev.types = ETHTOOL_PSE_C33; ++ priv->pcdev.nr_lines = SI3474_MAX_CHANS; ++ ++ ret = devm_pse_controller_register(dev, &priv->pcdev); ++ if (ret) { ++ dev_err(dev, "Failed to register PSE controller: 0x%x\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct i2c_device_id si3474_id[] = { ++ { "si3474" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, si3474_id); ++ ++static const struct of_device_id si3474_of_match[] = { ++ { ++ .compatible = "skyworks,si3474", ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, si3474_of_match); ++ ++static struct i2c_driver si3474_driver = { ++ .probe = si3474_i2c_probe, ++ .id_table = si3474_id, ++ .driver = { ++ .name = "si3474", ++ .of_match_table = si3474_of_match, ++ }, ++}; ++module_i2c_driver(si3474_driver); ++ ++MODULE_AUTHOR("Piotr Kubik "); ++MODULE_DESCRIPTION("Skyworks Si3474 PoE PSE Controller driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.12/626-24-v6.19-net-pse-pd-tps23881-Fix-current-measurement-scaling.patch b/target/linux/generic/backport-6.12/626-24-v6.19-net-pse-pd-tps23881-Fix-current-measurement-scaling.patch new file mode 100644 index 00000000000..5150da365f7 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-24-v6.19-net-pse-pd-tps23881-Fix-current-measurement-scaling.patch @@ -0,0 +1,31 @@ +From 2c95a756e0cfc19af6d0b32b0c6cf3bada334998 Mon Sep 17 00:00:00 2001 +From: Thomas Wismer +Date: Mon, 6 Oct 2025 22:40:29 +0200 +Subject: [PATCH] net: pse-pd: tps23881: Fix current measurement scaling + +The TPS23881 improves on the TPS23880 with current sense resistors reduced +from 255 mOhm to 200 mOhm. This has a direct impact on the scaling of the +current measurement. However, the latest TPS23881 data sheet from May 2023 +still shows the scaling of the TPS23880 model. + +Fixes: 7f076ce3f1733 ("net: pse-pd: tps23881: Add support for power limit and measurement features") +Signed-off-by: Thomas Wismer +Acked-by: Kory Maincent +Link: https://patch.msgid.link/20251006204029.7169-2-thomas@wismer.xyz +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/tps23881.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/pse-pd/tps23881.c ++++ b/drivers/net/pse-pd/tps23881.c +@@ -62,7 +62,7 @@ + #define TPS23881_REG_SRAM_DATA 0x61 + + #define TPS23881_UV_STEP 3662 +-#define TPS23881_NA_STEP 70190 ++#define TPS23881_NA_STEP 89500 + #define TPS23881_MW_STEP 500 + #define TPS23881_MIN_PI_PW_LIMIT_MW 2000 + diff --git a/target/linux/generic/backport-6.12/626-25-v6.19-net-pse-pd-pd692x0-Replace-__free-macro.patch b/target/linux/generic/backport-6.12/626-25-v6.19-net-pse-pd-pd692x0-Replace-__free-macro.patch new file mode 100644 index 00000000000..fafa5af2d1e --- /dev/null +++ b/target/linux/generic/backport-6.12/626-25-v6.19-net-pse-pd-pd692x0-Replace-__free-macro.patch @@ -0,0 +1,57 @@ +From f197902cd21ae833850679b216bb62c0d056bbb3 Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Mon, 13 Oct 2025 16:05:31 +0200 +Subject: [PATCH] net: pse-pd: pd692x0: Replace __free macro with explicit + kfree calls + +Replace __free(kfree) with explicit kfree() calls to follow the net +subsystem policy of avoiding automatic cleanup macros as described in +the documentation. + +Signed-off-by: Kory Maincent +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20251013-feature_pd692x0_reboot_keep_conf-v2-1-68ab082a93dd@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pd692x0.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/net/pse-pd/pd692x0.c ++++ b/drivers/net/pse-pd/pd692x0.c +@@ -1200,9 +1200,9 @@ static void pd692x0_managers_free_pw_bud + + static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev) + { +- struct pd692x0_manager *manager __free(kfree) = NULL; + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); + struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS]; ++ struct pd692x0_manager *manager; + int ret, nmanagers; + + /* Should we flash the port matrix */ +@@ -1216,7 +1216,7 @@ static int pd692x0_setup_pi_matrix(struc + + ret = pd692x0_of_get_managers(priv, manager); + if (ret < 0) +- return ret; ++ goto err_free_manager; + + nmanagers = ret; + ret = pd692x0_register_managers_regulator(priv, manager, nmanagers); +@@ -1236,12 +1236,15 @@ static int pd692x0_setup_pi_matrix(struc + goto err_managers_req_pw; + + pd692x0_of_put_managers(priv, manager, nmanagers); ++ kfree(manager); + return 0; + + err_managers_req_pw: + pd692x0_managers_free_pw_budget(priv); + err_of_managers: + pd692x0_of_put_managers(priv, manager, nmanagers); ++err_free_manager: ++ kfree(manager); + return ret; + } + diff --git a/target/linux/generic/backport-6.12/626-26-v6.19-net-pse-pd-pd692x0-Separate-configuration-parsing.patch b/target/linux/generic/backport-6.12/626-26-v6.19-net-pse-pd-pd692x0-Separate-configuration-parsing.patch new file mode 100644 index 00000000000..e88bbd03bfa --- /dev/null +++ b/target/linux/generic/backport-6.12/626-26-v6.19-net-pse-pd-pd692x0-Separate-configuration-parsing.patch @@ -0,0 +1,291 @@ +From 6fa1f8b64a47edd7d8420d8fd1008507aee2853e Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Mon, 13 Oct 2025 16:05:32 +0200 +Subject: [PATCH] net: pse-pd: pd692x0: Separate configuration parsing from + hardware setup + +Cache the port matrix configuration in driver private data to enable +PSE controller reconfiguration. This refactoring separates device tree +parsing from hardware configuration application, allowing settings to be +reapplied without reparsing the device tree. + +This refactoring is a prerequisite for preserving PSE configuration +across reboots to prevent power disruption to connected devices. + +Signed-off-by: Kory Maincent +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20251013-feature_pd692x0_reboot_keep_conf-v2-2-68ab082a93dd@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pd692x0.c | 115 +++++++++++++++++++++++------------ + 1 file changed, 76 insertions(+), 39 deletions(-) + +--- a/drivers/net/pse-pd/pd692x0.c ++++ b/drivers/net/pse-pd/pd692x0.c +@@ -85,6 +85,11 @@ enum { + PD692X0_MSG_CNT + }; + ++struct pd692x0_matrix { ++ u8 hw_port_a; ++ u8 hw_port_b; ++}; ++ + struct pd692x0_priv { + struct i2c_client *client; + struct pse_controller_dev pcdev; +@@ -101,6 +106,8 @@ struct pd692x0_priv { + enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS]; + struct regulator_dev *manager_reg[PD692X0_MAX_MANAGERS]; + int manager_pw_budget[PD692X0_MAX_MANAGERS]; ++ int nmanagers; ++ struct pd692x0_matrix *port_matrix; + }; + + /* Template list of communication messages. The non-null bytes defined here +@@ -809,11 +816,6 @@ struct pd692x0_manager { + int nports; + }; + +-struct pd692x0_matrix { +- u8 hw_port_a; +- u8 hw_port_b; +-}; +- + static int + pd692x0_of_get_ports_manager(struct pd692x0_priv *priv, + struct pd692x0_manager *manager, +@@ -903,7 +905,8 @@ pd692x0_of_get_managers(struct pd692x0_p + } + + of_node_put(managers_node); +- return nmanagers; ++ priv->nmanagers = nmanagers; ++ return 0; + + out: + for (i = 0; i < nmanagers; i++) { +@@ -963,8 +966,7 @@ pd692x0_register_manager_regulator(struc + + static int + pd692x0_register_managers_regulator(struct pd692x0_priv *priv, +- const struct pd692x0_manager *manager, +- int nmanagers) ++ const struct pd692x0_manager *manager) + { + struct device *dev = &priv->client->dev; + size_t reg_name_len; +@@ -975,7 +977,7 @@ pd692x0_register_managers_regulator(stru + */ + reg_name_len = strlen(dev_name(dev)) + 23; + +- for (i = 0; i < nmanagers; i++) { ++ for (i = 0; i < priv->nmanagers; i++) { + static const char * const regulators[] = { "vaux5", "vaux3p3" }; + struct regulator_dev *rdev; + char *reg_name; +@@ -1008,10 +1010,14 @@ pd692x0_register_managers_regulator(stru + } + + static int +-pd692x0_conf_manager_power_budget(struct pd692x0_priv *priv, int id, int pw) ++pd692x0_conf_manager_power_budget(struct pd692x0_priv *priv, int id) + { + struct pd692x0_msg msg, buf; +- int ret, pw_mW = pw / 1000; ++ int ret, pw_mW; ++ ++ pw_mW = priv->manager_pw_budget[id] / 1000; ++ if (!pw_mW) ++ return 0; + + msg = pd692x0_msg_template_list[PD692X0_MSG_GET_POWER_BANK]; + msg.data[0] = id; +@@ -1032,11 +1038,11 @@ pd692x0_conf_manager_power_budget(struct + } + + static int +-pd692x0_configure_managers(struct pd692x0_priv *priv, int nmanagers) ++pd692x0_req_managers_pw_budget(struct pd692x0_priv *priv) + { + int i, ret; + +- for (i = 0; i < nmanagers; i++) { ++ for (i = 0; i < priv->nmanagers; i++) { + struct regulator *supply = priv->manager_reg[i]->supply; + int pw_budget; + +@@ -1053,7 +1059,18 @@ pd692x0_configure_managers(struct pd692x + return ret; + + priv->manager_pw_budget[i] = pw_budget; +- ret = pd692x0_conf_manager_power_budget(priv, i, pw_budget); ++ } ++ ++ return 0; ++} ++ ++static int ++pd692x0_configure_managers(struct pd692x0_priv *priv) ++{ ++ int i, ret; ++ ++ for (i = 0; i < priv->nmanagers; i++) { ++ ret = pd692x0_conf_manager_power_budget(priv, i); + if (ret < 0) + return ret; + } +@@ -1101,10 +1118,9 @@ pd692x0_set_port_matrix(const struct pse + + static int + pd692x0_set_ports_matrix(struct pd692x0_priv *priv, +- const struct pd692x0_manager *manager, +- int nmanagers, +- struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS]) ++ const struct pd692x0_manager *manager) + { ++ struct pd692x0_matrix *port_matrix = priv->port_matrix; + struct pse_controller_dev *pcdev = &priv->pcdev; + int i, ret; + +@@ -1117,7 +1133,7 @@ pd692x0_set_ports_matrix(struct pd692x0_ + /* Update with values for every PSE PIs */ + for (i = 0; i < pcdev->nr_lines; i++) { + ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[0], +- manager, nmanagers, ++ manager, priv->nmanagers, + &port_matrix[i]); + if (ret) { + dev_err(&priv->client->dev, +@@ -1126,7 +1142,7 @@ pd692x0_set_ports_matrix(struct pd692x0_ + } + + ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[1], +- manager, nmanagers, ++ manager, priv->nmanagers, + &port_matrix[i]); + if (ret) { + dev_err(&priv->client->dev, +@@ -1139,9 +1155,9 @@ pd692x0_set_ports_matrix(struct pd692x0_ + } + + static int +-pd692x0_write_ports_matrix(struct pd692x0_priv *priv, +- const struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS]) ++pd692x0_write_ports_matrix(struct pd692x0_priv *priv) + { ++ struct pd692x0_matrix *port_matrix = priv->port_matrix; + struct pd692x0_msg msg, buf; + int ret, i; + +@@ -1166,13 +1182,32 @@ pd692x0_write_ports_matrix(struct pd692x + return 0; + } + ++static int pd692x0_hw_conf_init(struct pd692x0_priv *priv) ++{ ++ int ret; ++ ++ /* Is PD692x0 ready to be configured? */ ++ if (priv->fw_state != PD692X0_FW_OK && ++ priv->fw_state != PD692X0_FW_COMPLETE) ++ return 0; ++ ++ ret = pd692x0_configure_managers(priv); ++ if (ret) ++ return ret; ++ ++ ret = pd692x0_write_ports_matrix(priv); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ + static void pd692x0_of_put_managers(struct pd692x0_priv *priv, +- struct pd692x0_manager *manager, +- int nmanagers) ++ struct pd692x0_manager *manager) + { + int i, j; + +- for (i = 0; i < nmanagers; i++) { ++ for (i = 0; i < priv->nmanagers; i++) { + for (j = 0; j < manager[i].nports; j++) + of_node_put(manager[i].port_node[j]); + of_node_put(manager[i].node); +@@ -1201,48 +1236,50 @@ static void pd692x0_managers_free_pw_bud + static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev) + { + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); +- struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS]; ++ struct pd692x0_matrix *port_matrix; + struct pd692x0_manager *manager; +- int ret, nmanagers; +- +- /* Should we flash the port matrix */ +- if (priv->fw_state != PD692X0_FW_OK && +- priv->fw_state != PD692X0_FW_COMPLETE) +- return 0; ++ int ret; + + manager = kcalloc(PD692X0_MAX_MANAGERS, sizeof(*manager), GFP_KERNEL); + if (!manager) + return -ENOMEM; + ++ port_matrix = devm_kcalloc(&priv->client->dev, PD692X0_MAX_PIS, ++ sizeof(*port_matrix), GFP_KERNEL); ++ if (!port_matrix) { ++ ret = -ENOMEM; ++ goto err_free_manager; ++ } ++ priv->port_matrix = port_matrix; ++ + ret = pd692x0_of_get_managers(priv, manager); + if (ret < 0) + goto err_free_manager; + +- nmanagers = ret; +- ret = pd692x0_register_managers_regulator(priv, manager, nmanagers); ++ ret = pd692x0_register_managers_regulator(priv, manager); + if (ret) + goto err_of_managers; + +- ret = pd692x0_configure_managers(priv, nmanagers); ++ ret = pd692x0_req_managers_pw_budget(priv); + if (ret) + goto err_of_managers; + +- ret = pd692x0_set_ports_matrix(priv, manager, nmanagers, port_matrix); ++ ret = pd692x0_set_ports_matrix(priv, manager); + if (ret) + goto err_managers_req_pw; + +- ret = pd692x0_write_ports_matrix(priv, port_matrix); ++ ret = pd692x0_hw_conf_init(priv); + if (ret) + goto err_managers_req_pw; + +- pd692x0_of_put_managers(priv, manager, nmanagers); ++ pd692x0_of_put_managers(priv, manager); + kfree(manager); + return 0; + + err_managers_req_pw: + pd692x0_managers_free_pw_budget(priv); + err_of_managers: +- pd692x0_of_put_managers(priv, manager, nmanagers); ++ pd692x0_of_put_managers(priv, manager); + err_free_manager: + kfree(manager); + return ret; +@@ -1647,7 +1684,7 @@ static enum fw_upload_err pd692x0_fw_pol + return FW_UPLOAD_ERR_FW_INVALID; + } + +- ret = pd692x0_setup_pi_matrix(&priv->pcdev); ++ ret = pd692x0_hw_conf_init(priv); + if (ret < 0) { + dev_err(&client->dev, "Error configuring ports matrix (%pe)\n", + ERR_PTR(ret)); diff --git a/target/linux/generic/backport-6.12/626-27-v6.19-net-pse-pd-pd692x0-Preserve-PSE-configuration.patch b/target/linux/generic/backport-6.12/626-27-v6.19-net-pse-pd-pd692x0-Preserve-PSE-configuration.patch new file mode 100644 index 00000000000..4ae835e9322 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-27-v6.19-net-pse-pd-pd692x0-Preserve-PSE-configuration.patch @@ -0,0 +1,111 @@ +From 8f3d044b34fe99b894046edb84605456195cabc0 Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Mon, 13 Oct 2025 16:05:33 +0200 +Subject: [PATCH] net: pse-pd: pd692x0: Preserve PSE configuration across + reboots + +Detect when PSE hardware is already configured (user byte == 42) and +skip hardware initialization to prevent power interruption to connected +devices during system reboots. + +Previously, the driver would always reconfigure the PSE hardware on +probe, causing a port matrix reflash that resulted in temporary power +loss to all connected devices. This change maintains power continuity +by preserving existing configuration when the PSE has been previously +initialized. + +Signed-off-by: Kory Maincent +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20251013-feature_pd692x0_reboot_keep_conf-v2-3-68ab082a93dd@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pd692x0.c | 35 ++++++++++++++++++++++++++++++++--- + 1 file changed, 32 insertions(+), 3 deletions(-) + +--- a/drivers/net/pse-pd/pd692x0.c ++++ b/drivers/net/pse-pd/pd692x0.c +@@ -30,6 +30,8 @@ + #define PD692X0_FW_MIN_VER 5 + #define PD692X0_FW_PATCH_VER 5 + ++#define PD692X0_USER_BYTE 42 ++ + enum pd692x0_fw_state { + PD692X0_FW_UNKNOWN, + PD692X0_FW_OK, +@@ -80,6 +82,7 @@ enum { + PD692X0_MSG_GET_PORT_PARAM, + PD692X0_MSG_GET_POWER_BANK, + PD692X0_MSG_SET_POWER_BANK, ++ PD692X0_MSG_SET_USER_BYTE, + + /* add new message above here */ + PD692X0_MSG_CNT +@@ -103,6 +106,7 @@ struct pd692x0_priv { + bool last_cmd_key; + unsigned long last_cmd_key_time; + ++ bool cfg_saved; + enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS]; + struct regulator_dev *manager_reg[PD692X0_MAX_MANAGERS]; + int manager_pw_budget[PD692X0_MAX_MANAGERS]; +@@ -193,6 +197,12 @@ static const struct pd692x0_msg pd692x0_ + .key = PD692X0_KEY_CMD, + .sub = {0x07, 0x0b, 0x57}, + }, ++ [PD692X0_MSG_SET_USER_BYTE] = { ++ .key = PD692X0_KEY_PRG, ++ .sub = {0x41, PD692X0_USER_BYTE}, ++ .data = {0x4e, 0x4e, 0x4e, 0x4e, ++ 0x4e, 0x4e, 0x4e, 0x4e}, ++ }, + }; + + static u8 pd692x0_build_msg(struct pd692x0_msg *msg, u8 echo) +@@ -1233,6 +1243,15 @@ static void pd692x0_managers_free_pw_bud + } + } + ++static int ++pd692x0_save_user_byte(struct pd692x0_priv *priv) ++{ ++ struct pd692x0_msg msg, buf; ++ ++ msg = pd692x0_msg_template_list[PD692X0_MSG_SET_USER_BYTE]; ++ return pd692x0_sendrecv_msg(priv, &msg, &buf); ++} ++ + static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev) + { + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); +@@ -1268,9 +1287,16 @@ static int pd692x0_setup_pi_matrix(struc + if (ret) + goto err_managers_req_pw; + +- ret = pd692x0_hw_conf_init(priv); +- if (ret) +- goto err_managers_req_pw; ++ /* Do not init the conf if it is already saved */ ++ if (!priv->cfg_saved) { ++ ret = pd692x0_hw_conf_init(priv); ++ if (ret) ++ goto err_managers_req_pw; ++ ++ ret = pd692x0_save_user_byte(priv); ++ if (ret) ++ goto err_managers_req_pw; ++ } + + pd692x0_of_put_managers(priv, manager); + kfree(manager); +@@ -1793,6 +1819,9 @@ static int pd692x0_i2c_probe(struct i2c_ + } + } + ++ if (buf.data[2] == PD692X0_USER_BYTE) ++ priv->cfg_saved = true; ++ + priv->np = dev->of_node; + priv->pcdev.nr_lines = PD692X0_MAX_PIS; + priv->pcdev.owner = THIS_MODULE; diff --git a/target/linux/generic/backport-6.12/626-28-v6.19-net-pse-pd-tps23881-Add-support-for-TPS23881B.patch b/target/linux/generic/backport-6.12/626-28-v6.19-net-pse-pd-tps23881-Add-support-for-TPS23881B.patch new file mode 100644 index 00000000000..2c188db7a37 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-28-v6.19-net-pse-pd-tps23881-Add-support-for-TPS23881B.patch @@ -0,0 +1,170 @@ +From 4d07797faaa19aa8e80e10a04ca1a72c643ef5cf Mon Sep 17 00:00:00 2001 +From: Thomas Wismer +Date: Wed, 29 Oct 2025 22:23:09 +0100 +Subject: [PATCH] net: pse-pd: tps23881: Add support for TPS23881B + +The TPS23881B uses different firmware than the TPS23881. Trying to load the +TPS23881 firmware on a TPS23881B device fails and must be omitted. + +The TPS23881B ships with a more recent ROM firmware. Moreover, no updated +firmware has been released yet and so the firmware loading step must be +skipped. As of today, the TPS23881B is intended to use its ROM firmware. + +Signed-off-by: Thomas Wismer +Reviewed-by: Kory Maincent +Acked-by: Oleksij Rempel +Link: https://patch.msgid.link/20251029212312.108749-2-thomas@wismer.xyz +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/tps23881.c | 69 +++++++++++++++++++++++++++-------- + 1 file changed, 54 insertions(+), 15 deletions(-) + +--- a/drivers/net/pse-pd/tps23881.c ++++ b/drivers/net/pse-pd/tps23881.c +@@ -55,8 +55,6 @@ + #define TPS23881_REG_TPON BIT(0) + #define TPS23881_REG_FWREV 0x41 + #define TPS23881_REG_DEVID 0x43 +-#define TPS23881_REG_DEVID_MASK 0xF0 +-#define TPS23881_DEVICE_ID 0x02 + #define TPS23881_REG_CHAN1_CLASS 0x4c + #define TPS23881_REG_SRAM_CTRL 0x60 + #define TPS23881_REG_SRAM_DATA 0x61 +@@ -1012,8 +1010,28 @@ static const struct pse_controller_ops t + .pi_get_pw_req = tps23881_pi_get_pw_req, + }; + +-static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin"; +-static const char fw_sram_name[] = "ti/tps23881/tps23881-sram-14.bin"; ++struct tps23881_info { ++ u8 dev_id; /* device ID and silicon revision */ ++ const char *fw_parity_name; /* parity code firmware file name */ ++ const char *fw_sram_name; /* SRAM code firmware file name */ ++}; ++ ++enum tps23881_model { ++ TPS23881, ++ TPS23881B, ++}; ++ ++static const struct tps23881_info tps23881_info[] = { ++ [TPS23881] = { ++ .dev_id = 0x22, ++ .fw_parity_name = "ti/tps23881/tps23881-parity-14.bin", ++ .fw_sram_name = "ti/tps23881/tps23881-sram-14.bin", ++ }, ++ [TPS23881B] = { ++ .dev_id = 0x24, ++ /* skip SRAM load, ROM provides Clause 145 hardware-level support */ ++ }, ++}; + + struct tps23881_fw_conf { + u8 reg; +@@ -1085,16 +1103,17 @@ out: + return ret; + } + +-static int tps23881_flash_sram_fw(struct i2c_client *client) ++static int tps23881_flash_sram_fw(struct i2c_client *client, ++ const struct tps23881_info *info) + { + int ret; + +- ret = tps23881_flash_sram_fw_part(client, fw_parity_name, ++ ret = tps23881_flash_sram_fw_part(client, info->fw_parity_name, + tps23881_fw_parity_conf); + if (ret) + return ret; + +- ret = tps23881_flash_sram_fw_part(client, fw_sram_name, ++ ret = tps23881_flash_sram_fw_part(client, info->fw_sram_name, + tps23881_fw_sram_conf); + if (ret) + return ret; +@@ -1412,6 +1431,7 @@ static int tps23881_setup_irq(struct tps + static int tps23881_i2c_probe(struct i2c_client *client) + { + struct device *dev = &client->dev; ++ const struct tps23881_info *info; + struct tps23881_priv *priv; + struct gpio_desc *reset; + int ret; +@@ -1422,6 +1442,10 @@ static int tps23881_i2c_probe(struct i2c + return -ENXIO; + } + ++ info = i2c_get_match_data(client); ++ if (!info) ++ return -EINVAL; ++ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; +@@ -1440,7 +1464,7 @@ static int tps23881_i2c_probe(struct i2c + * to Load TPS2388x SRAM and Parity Code over I2C" (Rev E)) + * indicates we should delay that programming by at least 50ms. So + * we'll wait the entire 50ms here to ensure we're safe to go to the +- * SRAM loading proceedure. ++ * SRAM loading procedure. + */ + msleep(50); + } +@@ -1449,20 +1473,27 @@ static int tps23881_i2c_probe(struct i2c + if (ret < 0) + return ret; + +- if (FIELD_GET(TPS23881_REG_DEVID_MASK, ret) != TPS23881_DEVICE_ID) { ++ if (ret != info->dev_id) { + dev_err(dev, "Wrong device ID\n"); + return -ENXIO; + } + +- ret = tps23881_flash_sram_fw(client); +- if (ret < 0) +- return ret; ++ if (info->fw_sram_name) { ++ ret = tps23881_flash_sram_fw(client, info); ++ if (ret < 0) ++ return ret; ++ } + + ret = i2c_smbus_read_byte_data(client, TPS23881_REG_FWREV); + if (ret < 0) + return ret; + +- dev_info(&client->dev, "Firmware revision 0x%x\n", ret); ++ if (ret == 0xFF) { ++ dev_err(&client->dev, "Device entered safe mode\n"); ++ return -ENXIO; ++ } ++ dev_info(&client->dev, "Firmware revision 0x%x%s\n", ret, ++ ret == 0x00 ? " (ROM firmware)" : ""); + + /* Set configuration B, 16 bit access on a single device address */ + ret = i2c_smbus_read_byte_data(client, TPS23881_REG_GEN_MASK); +@@ -1498,13 +1529,21 @@ static int tps23881_i2c_probe(struct i2c + } + + static const struct i2c_device_id tps23881_id[] = { +- { "tps23881" }, ++ { "tps23881", .driver_data = (kernel_ulong_t)&tps23881_info[TPS23881] }, ++ { "tps23881b", .driver_data = (kernel_ulong_t)&tps23881_info[TPS23881B] }, + { } + }; + MODULE_DEVICE_TABLE(i2c, tps23881_id); + + static const struct of_device_id tps23881_of_match[] = { +- { .compatible = "ti,tps23881", }, ++ { ++ .compatible = "ti,tps23881", ++ .data = &tps23881_info[TPS23881] ++ }, ++ { ++ .compatible = "ti,tps23881b", ++ .data = &tps23881_info[TPS23881B] ++ }, + { }, + }; + MODULE_DEVICE_TABLE(of, tps23881_of_match); diff --git a/target/linux/generic/backport-6.12/626-v6.16-net-bridge-locally-receive-all-multicast-packets-if-.patch b/target/linux/generic/backport-6.12/626-v6.16-net-bridge-locally-receive-all-multicast-packets-if-.patch new file mode 100644 index 00000000000..d9bd8c73a01 --- /dev/null +++ b/target/linux/generic/backport-6.12/626-v6.16-net-bridge-locally-receive-all-multicast-packets-if-.patch @@ -0,0 +1,41 @@ +From a496d2f0fd612ab9e10700afe00dc9267bad788b Mon Sep 17 00:00:00 2001 +From: Shengyu Qu +Date: Mon, 14 Apr 2025 18:56:01 +0800 +Subject: net: bridge: locally receive all multicast packets if IFF_ALLMULTI is + set + +If multicast snooping is enabled, multicast packets may not always end up +on the local bridge interface, if the host is not a member of the multicast +group. Similar to how IFF_PROMISC allows all packets to be received +locally, let IFF_ALLMULTI allow all multicast packets to be received. + +OpenWrt uses a user space daemon for DHCPv6/RA/NDP handling, and in relay +mode it sets the ALLMULTI flag in order to receive all relevant queries on +the network. + +This works for normal network interfaces and non-snooping bridges, but not +snooping bridges (unless multicast routing is enabled). + +Reported-by: Felix Fietkau +Closes: https://github.com/openwrt/openwrt/issues/15857#issuecomment-2662851243 +Signed-off-by: Shengyu Qu +Reviewed-by: Ido Schimmel +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/OSZPR01MB8434308370ACAFA90A22980798B32@OSZPR01MB8434.jpnprd01.prod.outlook.com +Signed-off-by: Jakub Kicinski +--- + net/bridge/br_input.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -184,7 +184,8 @@ int br_handle_frame_finish(struct net *n + if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && + br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) { + if ((mdst && mdst->host_joined) || +- br_multicast_is_router(brmctx, skb)) { ++ br_multicast_is_router(brmctx, skb) || ++ br->dev->flags & IFF_ALLMULTI) { + local_rcv = true; + DEV_STATS_INC(br->dev, multicast); + } diff --git a/target/linux/generic/backport-6.12/627-01-v6.17-net-pse-pd-Add-ethtool_netlink_generated-header.patch b/target/linux/generic/backport-6.12/627-01-v6.17-net-pse-pd-Add-ethtool_netlink_generated-header.patch new file mode 100644 index 00000000000..f773fb6e4fc --- /dev/null +++ b/target/linux/generic/backport-6.12/627-01-v6.17-net-pse-pd-Add-ethtool_netlink_generated-header.patch @@ -0,0 +1,79 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: OpenWrt PSE-PD Backport +Date: Fri, 31 Jan 2025 00:00:00 +0000 +Subject: [PATCH] net: pse-pd: Add ethtool netlink definitions for PSE events + and priority + +GENERATED PATCH - OpenWrt PSE-PD Backport + +This patch: +1. Adds ETHTOOL_A_PSE_PW_D_ID, ETHTOOL_A_PSE_PRIO_MAX, ETHTOOL_A_PSE_PRIO to + the ETHTOOL_A_PSE_* enum in ethtool_netlink.h (backported from kernel 6.17+) +2. Creates include/uapi/linux/ethtool_netlink_generated.h with PSE event + definitions (in mainline 6.17+, this file is auto-generated) +3. Adds ETHTOOL_MSG_PSE_NTF to the kernel message types enum + +These definitions are required by PSE event reporting and priority patches. + +Signed-off-by: OpenWrt PSE-PD Backport +Signed-off-by: Carlo Szelinsky +--- + include/uapi/linux/ethtool_netlink.h | 4 ++ + include/uapi/linux/ethtool_netlink_generated.h | 31 ++++++++++++++++++++++++++ + 2 files changed, 35 insertions(+) + create mode 100644 include/uapi/linux/ethtool_netlink_generated.h + +--- a/include/uapi/linux/ethtool_netlink.h ++++ b/include/uapi/linux/ethtool_netlink.h +@@ -115,6 +115,7 @@ enum { + ETHTOOL_MSG_PHY_GET_REPLY, + ETHTOOL_MSG_PHY_NTF, + ++ ETHTOOL_MSG_PSE_NTF, + /* add new constants above here */ + __ETHTOOL_MSG_KERNEL_CNT, + ETHTOOL_MSG_KERNEL_MAX = __ETHTOOL_MSG_KERNEL_CNT - 1 +@@ -970,6 +971,9 @@ enum { + ETHTOOL_A_C33_PSE_EXT_SUBSTATE, /* u32 */ + ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT, /* u32 */ + ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES, /* nest - _C33_PSE_PW_LIMIT_* */ ++ ETHTOOL_A_PSE_PW_D_ID, /* u32 */ ++ ETHTOOL_A_PSE_PRIO_MAX, /* u32 */ ++ ETHTOOL_A_PSE_PRIO, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_PSE_CNT, +--- /dev/null ++++ b/include/uapi/linux/ethtool_netlink_generated.h +@@ -0,0 +1,31 @@ ++/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ ++/* PSE ethtool netlink definitions - OpenWrt backport for 6.12 */ ++ ++#ifndef _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H ++#define _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H ++ ++enum ethtool_pse_event { ++ ETHTOOL_PSE_EVENT_OVER_CURRENT = 1, ++ ETHTOOL_PSE_EVENT_OVER_TEMP = 2, ++ ETHTOOL_C33_PSE_EVENT_DETECTION = 4, ++ ETHTOOL_C33_PSE_EVENT_CLASSIFICATION = 8, ++ ETHTOOL_C33_PSE_EVENT_DISCONNECTION = 16, ++ ETHTOOL_PSE_EVENT_OVER_BUDGET = 32, ++ ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR = 64, ++}; ++ ++enum { ++ ETHTOOL_A_PSE_NTF_HEADER = 1, ++ ETHTOOL_A_PSE_NTF_EVENTS, ++ __ETHTOOL_A_PSE_NTF_CNT, ++ ETHTOOL_A_PSE_NTF_MAX = (__ETHTOOL_A_PSE_NTF_CNT - 1) ++}; ++ ++enum { ++ ETHTOOL_A_PSE_NTF_EVT_UNSPEC, ++ ETHTOOL_A_PSE_NTF_EVT_INDEX, ++ __ETHTOOL_A_PSE_NTF_EVT_CNT, ++ ETHTOOL_A_PSE_NTF_EVT_MAX = (__ETHTOOL_A_PSE_NTF_EVT_CNT - 1) ++}; ++ ++#endif /* _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H */ diff --git a/target/linux/generic/backport-6.12/627-02-v6.17-net-ethtool-Add-PSE-port-priority-support.patch b/target/linux/generic/backport-6.12/627-02-v6.17-net-ethtool-Add-PSE-port-priority-support.patch new file mode 100644 index 00000000000..06d9b11b909 --- /dev/null +++ b/target/linux/generic/backport-6.12/627-02-v6.17-net-ethtool-Add-PSE-port-priority-support.patch @@ -0,0 +1,68 @@ +# ADAPTED FOR OPENWRT 6.12 - Documentation changes removed +# Original commit: eeb0c8f72f49 +From eeb0c8f72f49a21984981188404cfd3700edbaff Mon Sep 17 00:00:00 2001 +From: "Kory Maincent (Dent Project)" +Date: Tue, 17 Jun 2025 14:12:07 +0200 +Subject: [PATCH] net: ethtool: Add PSE port priority support feature + +This patch expands the status information provided by ethtool for PSE c33 +with current port priority and max port priority. It also adds a call to +pse_ethtool_set_prio() to configure the PSE port priority. + +Signed-off-by: Kory Maincent (Dent Project) +Reviewed-by: Oleksij Rempel +Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-8-78a1a645e2ee@bootlin.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Carlo Szelinsky +--- + net/ethtool/pse-pd.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +--- a/net/ethtool/pse-pd.c ++++ b/net/ethtool/pse-pd.c +@@ -111,6 +111,9 @@ static int pse_reply_size(const struct e + len += st->c33_pw_limit_nb_ranges * + (nla_total_size(0) + + nla_total_size(sizeof(u32)) * 2); ++ if (st->prio_max) ++ /* _PSE_PRIO_MAX + _PSE_PRIO */ ++ len += nla_total_size(sizeof(u32)) * 2; + + return len; + } +@@ -205,6 +208,11 @@ static int pse_fill_reply(struct sk_buff + pse_put_pw_limit_ranges(skb, st)) + return -EMSGSIZE; + ++ if (st->prio_max && ++ (nla_put_u32(skb, ETHTOOL_A_PSE_PRIO_MAX, st->prio_max) || ++ nla_put_u32(skb, ETHTOOL_A_PSE_PRIO, st->prio))) ++ return -EMSGSIZE; ++ + return 0; + } + +@@ -226,6 +234,7 @@ const struct nla_policy ethnl_pse_set_po + NLA_POLICY_RANGE(NLA_U32, ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED, + ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED), + [ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT] = { .type = NLA_U32 }, ++ [ETHTOOL_A_PSE_PRIO] = { .type = NLA_U32 }, + }; + + static int +@@ -283,6 +292,15 @@ ethnl_set_pse(struct ethnl_req_info *req + if (ret) + return ret; + } ++ ++ if (tb[ETHTOOL_A_PSE_PRIO]) { ++ unsigned int prio; ++ ++ prio = nla_get_u32(tb[ETHTOOL_A_PSE_PRIO]); ++ ret = pse_ethtool_set_prio(phydev->psec, info->extack, prio); ++ if (ret) ++ return ret; ++ } + + /* These values are already validated by the ethnl_pse_set_policy */ + if (tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] || diff --git a/target/linux/generic/backport-6.12/630-v6.13-net-usb-qmi_wwan-add-Quectel-RG255C.patch b/target/linux/generic/backport-6.12/630-v6.13-net-usb-qmi_wwan-add-Quectel-RG255C.patch new file mode 100644 index 00000000000..a1eb835a559 --- /dev/null +++ b/target/linux/generic/backport-6.12/630-v6.13-net-usb-qmi_wwan-add-Quectel-RG255C.patch @@ -0,0 +1,47 @@ +From 5c964c8a97c12145104f5d2782aa1ffccf3a93dd Mon Sep 17 00:00:00 2001 +From: Martin Hou +Date: Mon, 16 Dec 2024 11:06:18 +0800 +Subject: [PATCH] net: usb: qmi_wwan: add Quectel RG255C + +Add support for Quectel RG255C which is based on Qualcomm SDX35 chip. +The composition is DM / NMEA / AT / QMI. + +T: Bus=01 Lev=01 Prnt=01 Port=04 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 +D: Ver= 2.01 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 +P: Vendor=2c7c ProdID=0316 Rev= 5.15 +S: Manufacturer=Quectel +S: Product=RG255C-CN +S: SerialNumber=c68192c1 +C:* #Ifs= 4 Cfg#= 1 Atr=a0 MxPwr=500mA +I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option +E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option +E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option +E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms +E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan +E: Ad=86(I) Atr=03(Int.) MxPS= 8 Ivl=32ms +E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms + +Signed-off-by: Martin Hou +Link: https://patch.msgid.link/tencent_17DDD787B48E8A5AB8379ED69E23A0CD9309@qq.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/qmi_wwan.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/usb/qmi_wwan.c ++++ b/drivers/net/usb/qmi_wwan.c +@@ -1442,6 +1442,7 @@ static const struct usb_device_id produc + {QMI_QUIRK_SET_DTR(0x2c7c, 0x0195, 4)}, /* Quectel EG95 */ + {QMI_FIXED_INTF(0x2c7c, 0x0296, 4)}, /* Quectel BG96 */ + {QMI_QUIRK_SET_DTR(0x2c7c, 0x030e, 4)}, /* Quectel EM05GV2 */ ++ {QMI_QUIRK_SET_DTR(0x2c7c, 0x0316, 3)}, /* Quectel RG255C */ + {QMI_QUIRK_SET_DTR(0x2cb7, 0x0104, 4)}, /* Fibocom NL678 series */ + {QMI_QUIRK_SET_DTR(0x2cb7, 0x0112, 0)}, /* Fibocom FG132 */ + {QMI_FIXED_INTF(0x0489, 0xe0b4, 0)}, /* Foxconn T77W968 LTE */ diff --git a/target/linux/generic/backport-6.12/630-v6.14-PM-sleep-wakeirq-Introduce-device-managed-variant-of.patch b/target/linux/generic/backport-6.12/630-v6.14-PM-sleep-wakeirq-Introduce-device-managed-variant-of.patch new file mode 100644 index 00000000000..06edffade34 --- /dev/null +++ b/target/linux/generic/backport-6.12/630-v6.14-PM-sleep-wakeirq-Introduce-device-managed-variant-of.patch @@ -0,0 +1,74 @@ +From fd8318a32573d73eb20637a0c80689de0dc98169 Mon Sep 17 00:00:00 2001 +From: Peng Fan +Date: Fri, 3 Jan 2025 16:41:13 +0800 +Subject: [PATCH] PM: sleep: wakeirq: Introduce device-managed variant of + dev_pm_set_wake_irq() + +Add device-managed variant of dev_pm_set_wake_irq which automatically +clear the wake irq on device destruction to simplify error handling +and resource management in drivers. + +Signed-off-by: Peng Fan +Link: https://patch.msgid.link/20250103-wake_irq-v2-1-e3aeff5e9966@nxp.com +Signed-off-by: Rafael J. Wysocki +--- + drivers/base/power/wakeirq.c | 26 ++++++++++++++++++++++++++ + include/linux/pm_wakeirq.h | 6 ++++++ + 2 files changed, 32 insertions(+) + +--- a/drivers/base/power/wakeirq.c ++++ b/drivers/base/power/wakeirq.c +@@ -106,6 +106,32 @@ void dev_pm_clear_wake_irq(struct device + } + EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq); + ++static void devm_pm_clear_wake_irq(void *dev) ++{ ++ dev_pm_clear_wake_irq(dev); ++} ++ ++/** ++ * devm_pm_set_wake_irq - device-managed variant of dev_pm_set_wake_irq ++ * @dev: Device entry ++ * @irq: Device IO interrupt ++ * ++ * ++ * Attach a device IO interrupt as a wake IRQ, same with dev_pm_set_wake_irq, ++ * but the device will be auto clear wake capability on driver detach. ++ */ ++int devm_pm_set_wake_irq(struct device *dev, int irq) ++{ ++ int ret; ++ ++ ret = dev_pm_set_wake_irq(dev, irq); ++ if (ret) ++ return ret; ++ ++ return devm_add_action_or_reset(dev, devm_pm_clear_wake_irq, dev); ++} ++EXPORT_SYMBOL_GPL(devm_pm_set_wake_irq); ++ + /** + * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts + * @irq: Device specific dedicated wake-up interrupt +--- a/include/linux/pm_wakeirq.h ++++ b/include/linux/pm_wakeirq.h +@@ -10,6 +10,7 @@ extern int dev_pm_set_wake_irq(struct de + extern int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq); + extern int dev_pm_set_dedicated_wake_irq_reverse(struct device *dev, int irq); + extern void dev_pm_clear_wake_irq(struct device *dev); ++extern int devm_pm_set_wake_irq(struct device *dev, int irq); + + #else /* !CONFIG_PM */ + +@@ -32,5 +33,10 @@ static inline void dev_pm_clear_wake_irq + { + } + ++static inline int devm_pm_set_wake_irq(struct device *dev, int irq) ++{ ++ return 0; ++} ++ + #endif /* CONFIG_PM */ + #endif /* _LINUX_PM_WAKEIRQ_H */ diff --git a/target/linux/generic/backport-6.12/700-01-v7.0-net-sched-Export-mq-functions-for-reuse.patch b/target/linux/generic/backport-6.12/700-01-v7.0-net-sched-Export-mq-functions-for-reuse.patch new file mode 100644 index 00000000000..01439cdbd71 --- /dev/null +++ b/target/linux/generic/backport-6.12/700-01-v7.0-net-sched-Export-mq-functions-for-reuse.patch @@ -0,0 +1,255 @@ +From 8560edcc05114e42d4d580baaf124bce8c291d55 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= +Date: Fri, 9 Jan 2026 14:15:30 +0100 +Subject: [PATCH 1/7] net/sched: Export mq functions for reuse +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To enable the cake_mq qdisc to reuse code from the mq qdisc, export a +bunch of functions from sch_mq. Split common functionality out from some +functions so it can be composed with other code, and export other +functions wholesale. To discourage wanton reuse, put the symbols into a +new NET_SCHED_INTERNAL namespace, and a sch_priv.h header file. + +No functional change intended. + +Reviewed-by: Willem de Bruijn +Signed-off-by: Toke Høiland-Jørgensen +Link: https://patch.msgid.link/20260109-mq-cake-sub-qdisc-v8-1-8d613fece5d8@redhat.com +Signed-off-by: Paolo Abeni +--- + MAINTAINERS | 1 + + include/net/sch_priv.h | 27 ++++++++++++++++ + net/sched/sch_mq.c | 71 +++++++++++++++++++++++++++++------------- + 3 files changed, 77 insertions(+), 22 deletions(-) + create mode 100644 include/net/sch_priv.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -22541,6 +22541,7 @@ L: netdev@vger.kernel.org + S: Maintained + F: include/net/pkt_cls.h + F: include/net/pkt_sched.h ++F: include/net/sch_priv.h + F: include/net/tc_act/ + F: include/uapi/linux/pkt_cls.h + F: include/uapi/linux/pkt_sched.h +--- /dev/null ++++ b/include/net/sch_priv.h +@@ -0,0 +1,27 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __NET_SCHED_PRIV_H ++#define __NET_SCHED_PRIV_H ++ ++#include ++ ++struct mq_sched { ++ struct Qdisc **qdiscs; ++}; ++ ++int mq_init_common(struct Qdisc *sch, struct nlattr *opt, ++ struct netlink_ext_ack *extack, ++ const struct Qdisc_ops *qdisc_ops); ++void mq_destroy_common(struct Qdisc *sch); ++void mq_attach(struct Qdisc *sch); ++void mq_dump_common(struct Qdisc *sch, struct sk_buff *skb); ++struct netdev_queue *mq_select_queue(struct Qdisc *sch, ++ struct tcmsg *tcm); ++struct Qdisc *mq_leaf(struct Qdisc *sch, unsigned long cl); ++unsigned long mq_find(struct Qdisc *sch, u32 classid); ++int mq_dump_class(struct Qdisc *sch, unsigned long cl, ++ struct sk_buff *skb, struct tcmsg *tcm); ++int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl, ++ struct gnet_dump *d); ++void mq_walk(struct Qdisc *sch, struct qdisc_walker *arg); ++ ++#endif +--- a/net/sched/sch_mq.c ++++ b/net/sched/sch_mq.c +@@ -15,11 +15,7 @@ + #include + #include + #include +-#include +- +-struct mq_sched { +- struct Qdisc **qdiscs; +-}; ++#include + + static int mq_offload(struct Qdisc *sch, enum tc_mq_command cmd) + { +@@ -49,23 +45,29 @@ static int mq_offload_stats(struct Qdisc + return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_MQ, &opt); + } + +-static void mq_destroy(struct Qdisc *sch) ++void mq_destroy_common(struct Qdisc *sch) + { + struct net_device *dev = qdisc_dev(sch); + struct mq_sched *priv = qdisc_priv(sch); + unsigned int ntx; + +- mq_offload(sch, TC_MQ_DESTROY); +- + if (!priv->qdiscs) + return; + for (ntx = 0; ntx < dev->num_tx_queues && priv->qdiscs[ntx]; ntx++) + qdisc_put(priv->qdiscs[ntx]); + kfree(priv->qdiscs); + } ++EXPORT_SYMBOL_NS_GPL(mq_destroy_common, NET_SCHED_INTERNAL); + +-static int mq_init(struct Qdisc *sch, struct nlattr *opt, +- struct netlink_ext_ack *extack) ++static void mq_destroy(struct Qdisc *sch) ++{ ++ mq_offload(sch, TC_MQ_DESTROY); ++ mq_destroy_common(sch); ++} ++ ++int mq_init_common(struct Qdisc *sch, struct nlattr *opt, ++ struct netlink_ext_ack *extack, ++ const struct Qdisc_ops *qdisc_ops) + { + struct net_device *dev = qdisc_dev(sch); + struct mq_sched *priv = qdisc_priv(sch); +@@ -87,7 +89,8 @@ static int mq_init(struct Qdisc *sch, st + + for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { + dev_queue = netdev_get_tx_queue(dev, ntx); +- qdisc = qdisc_create_dflt(dev_queue, get_default_qdisc_ops(dev, ntx), ++ qdisc = qdisc_create_dflt(dev_queue, ++ qdisc_ops ?: get_default_qdisc_ops(dev, ntx), + TC_H_MAKE(TC_H_MAJ(sch->handle), + TC_H_MIN(ntx + 1)), + extack); +@@ -98,12 +101,24 @@ static int mq_init(struct Qdisc *sch, st + } + + sch->flags |= TCQ_F_MQROOT; ++ return 0; ++} ++EXPORT_SYMBOL_NS_GPL(mq_init_common, NET_SCHED_INTERNAL); ++ ++static int mq_init(struct Qdisc *sch, struct nlattr *opt, ++ struct netlink_ext_ack *extack) ++{ ++ int ret; ++ ++ ret = mq_init_common(sch, opt, extack, NULL); ++ if (ret) ++ return ret; + + mq_offload(sch, TC_MQ_CREATE); + return 0; + } + +-static void mq_attach(struct Qdisc *sch) ++void mq_attach(struct Qdisc *sch) + { + struct net_device *dev = qdisc_dev(sch); + struct mq_sched *priv = qdisc_priv(sch); +@@ -124,8 +139,9 @@ static void mq_attach(struct Qdisc *sch) + kfree(priv->qdiscs); + priv->qdiscs = NULL; + } ++EXPORT_SYMBOL_NS_GPL(mq_attach, NET_SCHED_INTERNAL); + +-static int mq_dump(struct Qdisc *sch, struct sk_buff *skb) ++void mq_dump_common(struct Qdisc *sch, struct sk_buff *skb) + { + struct net_device *dev = qdisc_dev(sch); + struct Qdisc *qdisc; +@@ -152,7 +168,12 @@ static int mq_dump(struct Qdisc *sch, st + + spin_unlock_bh(qdisc_lock(qdisc)); + } ++} ++EXPORT_SYMBOL_NS_GPL(mq_dump_common, NET_SCHED_INTERNAL); + ++static int mq_dump(struct Qdisc *sch, struct sk_buff *skb) ++{ ++ mq_dump_common(sch, skb); + return mq_offload_stats(sch); + } + +@@ -166,11 +187,12 @@ static struct netdev_queue *mq_queue_get + return netdev_get_tx_queue(dev, ntx); + } + +-static struct netdev_queue *mq_select_queue(struct Qdisc *sch, +- struct tcmsg *tcm) ++struct netdev_queue *mq_select_queue(struct Qdisc *sch, ++ struct tcmsg *tcm) + { + return mq_queue_get(sch, TC_H_MIN(tcm->tcm_parent)); + } ++EXPORT_SYMBOL_NS_GPL(mq_select_queue, NET_SCHED_INTERNAL); + + static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new, + struct Qdisc **old, struct netlink_ext_ack *extack) +@@ -198,14 +220,15 @@ static int mq_graft(struct Qdisc *sch, u + return 0; + } + +-static struct Qdisc *mq_leaf(struct Qdisc *sch, unsigned long cl) ++struct Qdisc *mq_leaf(struct Qdisc *sch, unsigned long cl) + { + struct netdev_queue *dev_queue = mq_queue_get(sch, cl); + + return rtnl_dereference(dev_queue->qdisc_sleeping); + } ++EXPORT_SYMBOL_NS_GPL(mq_leaf, NET_SCHED_INTERNAL); + +-static unsigned long mq_find(struct Qdisc *sch, u32 classid) ++unsigned long mq_find(struct Qdisc *sch, u32 classid) + { + unsigned int ntx = TC_H_MIN(classid); + +@@ -213,9 +236,10 @@ static unsigned long mq_find(struct Qdis + return 0; + return ntx; + } ++EXPORT_SYMBOL_NS_GPL(mq_find, NET_SCHED_INTERNAL); + +-static int mq_dump_class(struct Qdisc *sch, unsigned long cl, +- struct sk_buff *skb, struct tcmsg *tcm) ++int mq_dump_class(struct Qdisc *sch, unsigned long cl, ++ struct sk_buff *skb, struct tcmsg *tcm) + { + struct netdev_queue *dev_queue = mq_queue_get(sch, cl); + +@@ -224,9 +248,10 @@ static int mq_dump_class(struct Qdisc *s + tcm->tcm_info = rtnl_dereference(dev_queue->qdisc_sleeping)->handle; + return 0; + } ++EXPORT_SYMBOL_NS_GPL(mq_dump_class, NET_SCHED_INTERNAL); + +-static int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl, +- struct gnet_dump *d) ++int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl, ++ struct gnet_dump *d) + { + struct netdev_queue *dev_queue = mq_queue_get(sch, cl); + +@@ -236,8 +261,9 @@ static int mq_dump_class_stats(struct Qd + return -1; + return 0; + } ++EXPORT_SYMBOL_NS_GPL(mq_dump_class_stats, NET_SCHED_INTERNAL); + +-static void mq_walk(struct Qdisc *sch, struct qdisc_walker *arg) ++void mq_walk(struct Qdisc *sch, struct qdisc_walker *arg) + { + struct net_device *dev = qdisc_dev(sch); + unsigned int ntx; +@@ -251,6 +277,7 @@ static void mq_walk(struct Qdisc *sch, s + break; + } + } ++EXPORT_SYMBOL_NS_GPL(mq_walk, NET_SCHED_INTERNAL); + + static const struct Qdisc_class_ops mq_class_ops = { + .select_queue = mq_select_queue, diff --git a/target/linux/generic/backport-6.12/700-02-v7.0-net-sched-sch_cake-Factor-out-config-variables-into-.patch b/target/linux/generic/backport-6.12/700-02-v7.0-net-sched-sch_cake-Factor-out-config-variables-into-.patch new file mode 100644 index 00000000000..8df110b5565 --- /dev/null +++ b/target/linux/generic/backport-6.12/700-02-v7.0-net-sched-sch_cake-Factor-out-config-variables-into-.patch @@ -0,0 +1,636 @@ +From 23090d3e9db80c2a374df1e636247eff9b6dd972 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= +Date: Fri, 9 Jan 2026 14:15:31 +0100 +Subject: [PATCH 2/7] net/sched: sch_cake: Factor out config variables into + separate struct +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Factor out all the user-configurable variables into a separate struct +and embed it into struct cake_sched_data. This is done in preparation +for sharing the configuration across multiple instances of cake in an mq +setup. + +No functional change is intended with this patch. + +Reviewed-by: Jamal Hadi Salim +Reviewed-by: Willem de Bruijn +Signed-off-by: Toke Høiland-Jørgensen +Link: https://patch.msgid.link/20260109-mq-cake-sub-qdisc-v8-2-8d613fece5d8@redhat.com +Signed-off-by: Paolo Abeni +--- + net/sched/sch_cake.c | 245 +++++++++++++++++++++++-------------------- + 1 file changed, 133 insertions(+), 112 deletions(-) + +--- a/net/sched/sch_cake.c ++++ b/net/sched/sch_cake.c +@@ -197,40 +197,42 @@ struct cake_tin_data { + u32 way_collisions; + }; /* number of tins is small, so size of this struct doesn't matter much */ + ++struct cake_sched_config { ++ u64 rate_bps; ++ u64 interval; ++ u64 target; ++ u32 buffer_config_limit; ++ u32 fwmark_mask; ++ u16 fwmark_shft; ++ s16 rate_overhead; ++ u16 rate_mpu; ++ u16 rate_flags; ++ u8 tin_mode; ++ u8 flow_mode; ++ u8 atm_mode; ++ u8 ack_filter; ++}; ++ + struct cake_sched_data { + struct tcf_proto __rcu *filter_list; /* optional external classifier */ + struct tcf_block *block; + struct cake_tin_data *tins; ++ struct cake_sched_config *config; + + struct cake_heap_entry overflow_heap[CAKE_QUEUES * CAKE_MAX_TINS]; +- u16 overflow_timeout; +- +- u16 tin_cnt; +- u8 tin_mode; +- u8 flow_mode; +- u8 ack_filter; +- u8 atm_mode; +- +- u32 fwmark_mask; +- u16 fwmark_shft; + + /* time_next = time_this + ((len * rate_ns) >> rate_shft) */ +- u16 rate_shft; + ktime_t time_next_packet; + ktime_t failsafe_next_packet; + u64 rate_ns; +- u64 rate_bps; +- u16 rate_flags; +- s16 rate_overhead; +- u16 rate_mpu; +- u64 interval; +- u64 target; ++ u16 rate_shft; ++ u16 overflow_timeout; ++ u16 tin_cnt; + + /* resource tracking */ + u32 buffer_used; + u32 buffer_max_used; + u32 buffer_limit; +- u32 buffer_config_limit; + + /* indices for dequeue */ + u16 cur_tin; +@@ -1195,7 +1197,7 @@ static bool cake_tcph_may_drop(const str + static struct sk_buff *cake_ack_filter(struct cake_sched_data *q, + struct cake_flow *flow) + { +- bool aggressive = q->ack_filter == CAKE_ACK_AGGRESSIVE; ++ bool aggressive = q->config->ack_filter == CAKE_ACK_AGGRESSIVE; + struct sk_buff *elig_ack = NULL, *elig_ack_prev = NULL; + struct sk_buff *skb_check, *skb_prev = NULL; + const struct ipv6hdr *ipv6h, *ipv6h_check; +@@ -1355,15 +1357,17 @@ static u64 cake_ewma(u64 avg, u64 sample + return avg; + } + +-static u32 cake_calc_overhead(struct cake_sched_data *q, u32 len, u32 off) ++static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off) + { ++ struct cake_sched_config *q = qd->config; ++ + if (q->rate_flags & CAKE_FLAG_OVERHEAD) + len -= off; + +- if (q->max_netlen < len) +- q->max_netlen = len; +- if (q->min_netlen > len) +- q->min_netlen = len; ++ if (qd->max_netlen < len) ++ qd->max_netlen = len; ++ if (qd->min_netlen > len) ++ qd->min_netlen = len; + + len += q->rate_overhead; + +@@ -1382,10 +1386,10 @@ static u32 cake_calc_overhead(struct cak + len += (len + 63) / 64; + } + +- if (q->max_adjlen < len) +- q->max_adjlen = len; +- if (q->min_adjlen > len) +- q->min_adjlen = len; ++ if (qd->max_adjlen < len) ++ qd->max_adjlen = len; ++ if (qd->min_adjlen > len) ++ qd->min_adjlen = len; + + return len; + } +@@ -1587,7 +1591,7 @@ static unsigned int cake_drop(struct Qdi + b->tin_dropped++; + sch->qstats.drops++; + +- if (q->rate_flags & CAKE_FLAG_INGRESS) ++ if (q->config->rate_flags & CAKE_FLAG_INGRESS) + cake_advance_shaper(q, b, skb, now, true); + + __qdisc_drop(skb, to_free); +@@ -1657,7 +1661,8 @@ static u8 cake_handle_diffserv(struct sk + static struct cake_tin_data *cake_select_tin(struct Qdisc *sch, + struct sk_buff *skb) + { +- struct cake_sched_data *q = qdisc_priv(sch); ++ struct cake_sched_data *qd = qdisc_priv(sch); ++ struct cake_sched_config *q = qd->config; + u32 tin, mark; + bool wash; + u8 dscp; +@@ -1674,24 +1679,24 @@ static struct cake_tin_data *cake_select + if (q->tin_mode == CAKE_DIFFSERV_BESTEFFORT) + tin = 0; + +- else if (mark && mark <= q->tin_cnt) +- tin = q->tin_order[mark - 1]; ++ else if (mark && mark <= qd->tin_cnt) ++ tin = qd->tin_order[mark - 1]; + + else if (TC_H_MAJ(skb->priority) == sch->handle && + TC_H_MIN(skb->priority) > 0 && +- TC_H_MIN(skb->priority) <= q->tin_cnt) +- tin = q->tin_order[TC_H_MIN(skb->priority) - 1]; ++ TC_H_MIN(skb->priority) <= qd->tin_cnt) ++ tin = qd->tin_order[TC_H_MIN(skb->priority) - 1]; + + else { + if (!wash) + dscp = cake_handle_diffserv(skb, wash); +- tin = q->tin_index[dscp]; ++ tin = qd->tin_index[dscp]; + +- if (unlikely(tin >= q->tin_cnt)) ++ if (unlikely(tin >= qd->tin_cnt)) + tin = 0; + } + +- return &q->tins[tin]; ++ return &qd->tins[tin]; + } + + static u32 cake_classify(struct Qdisc *sch, struct cake_tin_data **t, +@@ -1747,7 +1752,7 @@ static s32 cake_enqueue(struct sk_buff * + bool same_flow = false; + + /* choose flow to insert into */ +- idx = cake_classify(sch, &b, skb, q->flow_mode, &ret); ++ idx = cake_classify(sch, &b, skb, q->config->flow_mode, &ret); + if (idx == 0) { + if (ret & __NET_XMIT_BYPASS) + qdisc_qstats_drop(sch); +@@ -1782,7 +1787,7 @@ static s32 cake_enqueue(struct sk_buff * + if (unlikely(len > b->max_skblen)) + b->max_skblen = len; + +- if (skb_is_gso(skb) && q->rate_flags & CAKE_FLAG_SPLIT_GSO) { ++ if (skb_is_gso(skb) && q->config->rate_flags & CAKE_FLAG_SPLIT_GSO) { + struct sk_buff *segs, *nskb; + netdev_features_t features = netif_skb_features(skb); + unsigned int slen = 0, numsegs = 0; +@@ -1823,7 +1828,7 @@ static s32 cake_enqueue(struct sk_buff * + get_cobalt_cb(skb)->adjusted_len = cake_overhead(q, skb); + flow_queue_add(flow, skb); + +- if (q->ack_filter) ++ if (q->config->ack_filter) + ack = cake_ack_filter(q, flow); + + if (ack) { +@@ -1832,7 +1837,7 @@ static s32 cake_enqueue(struct sk_buff * + ack_pkt_len = qdisc_pkt_len(ack); + b->bytes += ack_pkt_len; + q->buffer_used += skb->truesize - ack->truesize; +- if (q->rate_flags & CAKE_FLAG_INGRESS) ++ if (q->config->rate_flags & CAKE_FLAG_INGRESS) + cake_advance_shaper(q, b, ack, now, true); + + qdisc_tree_reduce_backlog(sch, 1, ack_pkt_len); +@@ -1855,7 +1860,7 @@ static s32 cake_enqueue(struct sk_buff * + cake_heapify_up(q, b->overflow_idx[idx]); + + /* incoming bandwidth capacity estimate */ +- if (q->rate_flags & CAKE_FLAG_AUTORATE_INGRESS) { ++ if (q->config->rate_flags & CAKE_FLAG_AUTORATE_INGRESS) { + u64 packet_interval = \ + ktime_to_ns(ktime_sub(now, q->last_packet_time)); + +@@ -1887,7 +1892,7 @@ static s32 cake_enqueue(struct sk_buff * + if (ktime_after(now, + ktime_add_ms(q->last_reconfig_time, + 250))) { +- q->rate_bps = (q->avg_peak_bandwidth * 15) >> 4; ++ q->config->rate_bps = (q->avg_peak_bandwidth * 15) >> 4; + cake_reconfigure(sch); + } + } +@@ -1907,7 +1912,7 @@ static s32 cake_enqueue(struct sk_buff * + flow->set = CAKE_SET_SPARSE; + b->sparse_flow_count++; + +- flow->deficit = cake_get_flow_quantum(b, flow, q->flow_mode); ++ flow->deficit = cake_get_flow_quantum(b, flow, q->config->flow_mode); + } else if (flow->set == CAKE_SET_SPARSE_WAIT) { + /* this flow was empty, accounted as a sparse flow, but actually + * in the bulk rotation. +@@ -1916,8 +1921,8 @@ static s32 cake_enqueue(struct sk_buff * + b->sparse_flow_count--; + b->bulk_flow_count++; + +- cake_inc_srchost_bulk_flow_count(b, flow, q->flow_mode); +- cake_inc_dsthost_bulk_flow_count(b, flow, q->flow_mode); ++ cake_inc_srchost_bulk_flow_count(b, flow, q->config->flow_mode); ++ cake_inc_dsthost_bulk_flow_count(b, flow, q->config->flow_mode); + } + + if (q->buffer_used > q->buffer_max_used) +@@ -2103,8 +2108,8 @@ retry: + b->sparse_flow_count--; + b->bulk_flow_count++; + +- cake_inc_srchost_bulk_flow_count(b, flow, q->flow_mode); +- cake_inc_dsthost_bulk_flow_count(b, flow, q->flow_mode); ++ cake_inc_srchost_bulk_flow_count(b, flow, q->config->flow_mode); ++ cake_inc_dsthost_bulk_flow_count(b, flow, q->config->flow_mode); + + flow->set = CAKE_SET_BULK; + } else { +@@ -2116,7 +2121,7 @@ retry: + } + } + +- flow->deficit += cake_get_flow_quantum(b, flow, q->flow_mode); ++ flow->deficit += cake_get_flow_quantum(b, flow, q->config->flow_mode); + list_move_tail(&flow->flowchain, &b->old_flows); + + goto retry; +@@ -2140,8 +2145,8 @@ retry: + if (flow->set == CAKE_SET_BULK) { + b->bulk_flow_count--; + +- cake_dec_srchost_bulk_flow_count(b, flow, q->flow_mode); +- cake_dec_dsthost_bulk_flow_count(b, flow, q->flow_mode); ++ cake_dec_srchost_bulk_flow_count(b, flow, q->config->flow_mode); ++ cake_dec_dsthost_bulk_flow_count(b, flow, q->config->flow_mode); + + b->decaying_flow_count++; + } else if (flow->set == CAKE_SET_SPARSE || +@@ -2159,8 +2164,8 @@ retry: + else if (flow->set == CAKE_SET_BULK) { + b->bulk_flow_count--; + +- cake_dec_srchost_bulk_flow_count(b, flow, q->flow_mode); +- cake_dec_dsthost_bulk_flow_count(b, flow, q->flow_mode); ++ cake_dec_srchost_bulk_flow_count(b, flow, q->config->flow_mode); ++ cake_dec_dsthost_bulk_flow_count(b, flow, q->config->flow_mode); + } else + b->decaying_flow_count--; + +@@ -2172,13 +2177,13 @@ retry: + /* Last packet in queue may be marked, shouldn't be dropped */ + if (!cobalt_should_drop(&flow->cvars, &b->cparams, now, skb, + (b->bulk_flow_count * +- !!(q->rate_flags & ++ !!(q->config->rate_flags & + CAKE_FLAG_INGRESS))) || + !flow->head) + break; + + /* drop this packet, get another one */ +- if (q->rate_flags & CAKE_FLAG_INGRESS) { ++ if (q->config->rate_flags & CAKE_FLAG_INGRESS) { + len = cake_advance_shaper(q, b, skb, + now, true); + flow->deficit -= len; +@@ -2189,7 +2194,7 @@ retry: + qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(skb)); + qdisc_qstats_drop(sch); + kfree_skb(skb); +- if (q->rate_flags & CAKE_FLAG_INGRESS) ++ if (q->config->rate_flags & CAKE_FLAG_INGRESS) + goto retry; + } + +@@ -2311,7 +2316,7 @@ static int cake_config_besteffort(struct + struct cake_sched_data *q = qdisc_priv(sch); + struct cake_tin_data *b = &q->tins[0]; + u32 mtu = psched_mtu(qdisc_dev(sch)); +- u64 rate = q->rate_bps; ++ u64 rate = q->config->rate_bps; + + q->tin_cnt = 1; + +@@ -2319,7 +2324,7 @@ static int cake_config_besteffort(struct + q->tin_order = normal_order; + + cake_set_rate(b, rate, mtu, +- us_to_ns(q->target), us_to_ns(q->interval)); ++ us_to_ns(q->config->target), us_to_ns(q->config->interval)); + b->tin_quantum = 65535; + + return 0; +@@ -2330,7 +2335,7 @@ static int cake_config_precedence(struct + /* convert high-level (user visible) parameters into internal format */ + struct cake_sched_data *q = qdisc_priv(sch); + u32 mtu = psched_mtu(qdisc_dev(sch)); +- u64 rate = q->rate_bps; ++ u64 rate = q->config->rate_bps; + u32 quantum = 256; + u32 i; + +@@ -2341,8 +2346,8 @@ static int cake_config_precedence(struct + for (i = 0; i < q->tin_cnt; i++) { + struct cake_tin_data *b = &q->tins[i]; + +- cake_set_rate(b, rate, mtu, us_to_ns(q->target), +- us_to_ns(q->interval)); ++ cake_set_rate(b, rate, mtu, us_to_ns(q->config->target), ++ us_to_ns(q->config->interval)); + + b->tin_quantum = max_t(u16, 1U, quantum); + +@@ -2419,7 +2424,7 @@ static int cake_config_diffserv8(struct + + struct cake_sched_data *q = qdisc_priv(sch); + u32 mtu = psched_mtu(qdisc_dev(sch)); +- u64 rate = q->rate_bps; ++ u64 rate = q->config->rate_bps; + u32 quantum = 256; + u32 i; + +@@ -2433,8 +2438,8 @@ static int cake_config_diffserv8(struct + for (i = 0; i < q->tin_cnt; i++) { + struct cake_tin_data *b = &q->tins[i]; + +- cake_set_rate(b, rate, mtu, us_to_ns(q->target), +- us_to_ns(q->interval)); ++ cake_set_rate(b, rate, mtu, us_to_ns(q->config->target), ++ us_to_ns(q->config->interval)); + + b->tin_quantum = max_t(u16, 1U, quantum); + +@@ -2463,7 +2468,7 @@ static int cake_config_diffserv4(struct + + struct cake_sched_data *q = qdisc_priv(sch); + u32 mtu = psched_mtu(qdisc_dev(sch)); +- u64 rate = q->rate_bps; ++ u64 rate = q->config->rate_bps; + u32 quantum = 1024; + + q->tin_cnt = 4; +@@ -2474,13 +2479,13 @@ static int cake_config_diffserv4(struct + + /* class characteristics */ + cake_set_rate(&q->tins[0], rate, mtu, +- us_to_ns(q->target), us_to_ns(q->interval)); ++ us_to_ns(q->config->target), us_to_ns(q->config->interval)); + cake_set_rate(&q->tins[1], rate >> 4, mtu, +- us_to_ns(q->target), us_to_ns(q->interval)); ++ us_to_ns(q->config->target), us_to_ns(q->config->interval)); + cake_set_rate(&q->tins[2], rate >> 1, mtu, +- us_to_ns(q->target), us_to_ns(q->interval)); ++ us_to_ns(q->config->target), us_to_ns(q->config->interval)); + cake_set_rate(&q->tins[3], rate >> 2, mtu, +- us_to_ns(q->target), us_to_ns(q->interval)); ++ us_to_ns(q->config->target), us_to_ns(q->config->interval)); + + /* bandwidth-sharing weights */ + q->tins[0].tin_quantum = quantum; +@@ -2500,7 +2505,7 @@ static int cake_config_diffserv3(struct + */ + struct cake_sched_data *q = qdisc_priv(sch); + u32 mtu = psched_mtu(qdisc_dev(sch)); +- u64 rate = q->rate_bps; ++ u64 rate = q->config->rate_bps; + u32 quantum = 1024; + + q->tin_cnt = 3; +@@ -2511,11 +2516,11 @@ static int cake_config_diffserv3(struct + + /* class characteristics */ + cake_set_rate(&q->tins[0], rate, mtu, +- us_to_ns(q->target), us_to_ns(q->interval)); ++ us_to_ns(q->config->target), us_to_ns(q->config->interval)); + cake_set_rate(&q->tins[1], rate >> 4, mtu, +- us_to_ns(q->target), us_to_ns(q->interval)); ++ us_to_ns(q->config->target), us_to_ns(q->config->interval)); + cake_set_rate(&q->tins[2], rate >> 2, mtu, +- us_to_ns(q->target), us_to_ns(q->interval)); ++ us_to_ns(q->config->target), us_to_ns(q->config->interval)); + + /* bandwidth-sharing weights */ + q->tins[0].tin_quantum = quantum; +@@ -2527,7 +2532,8 @@ static int cake_config_diffserv3(struct + + static void cake_reconfigure(struct Qdisc *sch) + { +- struct cake_sched_data *q = qdisc_priv(sch); ++ struct cake_sched_data *qd = qdisc_priv(sch); ++ struct cake_sched_config *q = qd->config; + int c, ft; + + switch (q->tin_mode) { +@@ -2553,36 +2559,37 @@ static void cake_reconfigure(struct Qdis + break; + } + +- for (c = q->tin_cnt; c < CAKE_MAX_TINS; c++) { ++ for (c = qd->tin_cnt; c < CAKE_MAX_TINS; c++) { + cake_clear_tin(sch, c); +- q->tins[c].cparams.mtu_time = q->tins[ft].cparams.mtu_time; ++ qd->tins[c].cparams.mtu_time = qd->tins[ft].cparams.mtu_time; + } + +- q->rate_ns = q->tins[ft].tin_rate_ns; +- q->rate_shft = q->tins[ft].tin_rate_shft; ++ qd->rate_ns = qd->tins[ft].tin_rate_ns; ++ qd->rate_shft = qd->tins[ft].tin_rate_shft; + + if (q->buffer_config_limit) { +- q->buffer_limit = q->buffer_config_limit; ++ qd->buffer_limit = q->buffer_config_limit; + } else if (q->rate_bps) { + u64 t = q->rate_bps * q->interval; + + do_div(t, USEC_PER_SEC / 4); +- q->buffer_limit = max_t(u32, t, 4U << 20); ++ qd->buffer_limit = max_t(u32, t, 4U << 20); + } else { +- q->buffer_limit = ~0; ++ qd->buffer_limit = ~0; + } + + sch->flags &= ~TCQ_F_CAN_BYPASS; + +- q->buffer_limit = min(q->buffer_limit, +- max(sch->limit * psched_mtu(qdisc_dev(sch)), +- q->buffer_config_limit)); ++ qd->buffer_limit = min(qd->buffer_limit, ++ max(sch->limit * psched_mtu(qdisc_dev(sch)), ++ q->buffer_config_limit)); + } + + static int cake_change(struct Qdisc *sch, struct nlattr *opt, + struct netlink_ext_ack *extack) + { +- struct cake_sched_data *q = qdisc_priv(sch); ++ struct cake_sched_data *qd = qdisc_priv(sch); ++ struct cake_sched_config *q = qd->config; + struct nlattr *tb[TCA_CAKE_MAX + 1]; + u16 rate_flags; + u8 flow_mode; +@@ -2636,19 +2643,19 @@ static int cake_change(struct Qdisc *sch + nla_get_s32(tb[TCA_CAKE_OVERHEAD])); + rate_flags |= CAKE_FLAG_OVERHEAD; + +- q->max_netlen = 0; +- q->max_adjlen = 0; +- q->min_netlen = ~0; +- q->min_adjlen = ~0; ++ qd->max_netlen = 0; ++ qd->max_adjlen = 0; ++ qd->min_netlen = ~0; ++ qd->min_adjlen = ~0; + } + + if (tb[TCA_CAKE_RAW]) { + rate_flags &= ~CAKE_FLAG_OVERHEAD; + +- q->max_netlen = 0; +- q->max_adjlen = 0; +- q->min_netlen = ~0; +- q->min_adjlen = ~0; ++ qd->max_netlen = 0; ++ qd->max_adjlen = 0; ++ qd->min_netlen = ~0; ++ qd->min_adjlen = ~0; + } + + if (tb[TCA_CAKE_MPU]) +@@ -2704,7 +2711,7 @@ static int cake_change(struct Qdisc *sch + + WRITE_ONCE(q->rate_flags, rate_flags); + WRITE_ONCE(q->flow_mode, flow_mode); +- if (q->tins) { ++ if (qd->tins) { + sch_tree_lock(sch); + cake_reconfigure(sch); + sch_tree_unlock(sch); +@@ -2720,14 +2727,20 @@ static void cake_destroy(struct Qdisc *s + qdisc_watchdog_cancel(&q->watchdog); + tcf_block_put(q->block); + kvfree(q->tins); ++ kvfree(q->config); + } + + static int cake_init(struct Qdisc *sch, struct nlattr *opt, + struct netlink_ext_ack *extack) + { +- struct cake_sched_data *q = qdisc_priv(sch); ++ struct cake_sched_data *qd = qdisc_priv(sch); ++ struct cake_sched_config *q; + int i, j, err; + ++ q = kzalloc(sizeof(*q), GFP_KERNEL); ++ if (!q) ++ return -ENOMEM; ++ + sch->limit = 10240; + q->tin_mode = CAKE_DIFFSERV_DIFFSERV3; + q->flow_mode = CAKE_FLOW_TRIPLE; +@@ -2739,33 +2752,36 @@ static int cake_init(struct Qdisc *sch, + * for 5 to 10% of interval + */ + q->rate_flags |= CAKE_FLAG_SPLIT_GSO; +- q->cur_tin = 0; +- q->cur_flow = 0; ++ qd->cur_tin = 0; ++ qd->cur_flow = 0; ++ qd->config = q; + +- qdisc_watchdog_init(&q->watchdog, sch); ++ qdisc_watchdog_init(&qd->watchdog, sch); + + if (opt) { + err = cake_change(sch, opt, extack); + + if (err) +- return err; ++ goto err; + } + +- err = tcf_block_get(&q->block, &q->filter_list, sch, extack); ++ err = tcf_block_get(&qd->block, &qd->filter_list, sch, extack); + if (err) +- return err; ++ goto err; + + quantum_div[0] = ~0; + for (i = 1; i <= CAKE_QUEUES; i++) + quantum_div[i] = 65535 / i; + +- q->tins = kvcalloc(CAKE_MAX_TINS, sizeof(struct cake_tin_data), +- GFP_KERNEL); +- if (!q->tins) +- return -ENOMEM; ++ qd->tins = kvcalloc(CAKE_MAX_TINS, sizeof(struct cake_tin_data), ++ GFP_KERNEL); ++ if (!qd->tins) { ++ err = -ENOMEM; ++ goto err; ++ } + + for (i = 0; i < CAKE_MAX_TINS; i++) { +- struct cake_tin_data *b = q->tins + i; ++ struct cake_tin_data *b = qd->tins + i; + + INIT_LIST_HEAD(&b->new_flows); + INIT_LIST_HEAD(&b->old_flows); +@@ -2781,22 +2797,27 @@ static int cake_init(struct Qdisc *sch, + INIT_LIST_HEAD(&flow->flowchain); + cobalt_vars_init(&flow->cvars); + +- q->overflow_heap[k].t = i; +- q->overflow_heap[k].b = j; ++ qd->overflow_heap[k].t = i; ++ qd->overflow_heap[k].b = j; + b->overflow_idx[j] = k; + } + } + + cake_reconfigure(sch); +- q->avg_peak_bandwidth = q->rate_bps; +- q->min_netlen = ~0; +- q->min_adjlen = ~0; ++ qd->avg_peak_bandwidth = q->rate_bps; ++ qd->min_netlen = ~0; ++ qd->min_adjlen = ~0; + return 0; ++err: ++ kvfree(qd->config); ++ qd->config = NULL; ++ return err; + } + + static int cake_dump(struct Qdisc *sch, struct sk_buff *skb) + { +- struct cake_sched_data *q = qdisc_priv(sch); ++ struct cake_sched_data *qd = qdisc_priv(sch); ++ struct cake_sched_config *q = qd->config; + struct nlattr *opts; + u16 rate_flags; + u8 flow_mode; diff --git a/target/linux/generic/backport-6.12/700-03-v7.0-net-sched-sch_cake-Add-cake_mq-qdisc-for-using-cake-.patch b/target/linux/generic/backport-6.12/700-03-v7.0-net-sched-sch_cake-Add-cake_mq-qdisc-for-using-cake-.patch new file mode 100644 index 00000000000..645b27f8742 --- /dev/null +++ b/target/linux/generic/backport-6.12/700-03-v7.0-net-sched-sch_cake-Add-cake_mq-qdisc-for-using-cake-.patch @@ -0,0 +1,130 @@ +From c076e98c66afe06e41e118eb96d6b15019b822f2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= +Date: Fri, 9 Jan 2026 14:15:32 +0100 +Subject: [PATCH 3/7] net/sched: sch_cake: Add cake_mq qdisc for using cake on + mq devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add a cake_mq qdisc which installs cake instances on each hardware +queue on a multi-queue device. + +This is just a copy of sch_mq that installs cake instead of the default +qdisc on each queue. Subsequent commits will add sharing of the config +between cake instances, as well as a multi-queue aware shaper algorithm. + +Reviewed-by: Willem de Bruijn +Signed-off-by: Toke Høiland-Jørgensen +Link: https://patch.msgid.link/20260109-mq-cake-sub-qdisc-v8-3-8d613fece5d8@redhat.com +Signed-off-by: Paolo Abeni +--- + net/sched/sch_cake.c | 79 +++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 78 insertions(+), 1 deletion(-) + +--- a/net/sched/sch_cake.c ++++ b/net/sched/sch_cake.c +@@ -67,6 +67,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -3154,14 +3155,89 @@ static struct Qdisc_ops cake_qdisc_ops _ + }; + MODULE_ALIAS_NET_SCH("cake"); + ++struct cake_mq_sched { ++ struct mq_sched mq_priv; /* must be first */ ++}; ++ ++static void cake_mq_destroy(struct Qdisc *sch) ++{ ++ mq_destroy_common(sch); ++} ++ ++static int cake_mq_init(struct Qdisc *sch, struct nlattr *opt, ++ struct netlink_ext_ack *extack) ++{ ++ int ret; ++ ++ ret = mq_init_common(sch, opt, extack, &cake_qdisc_ops); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int cake_mq_dump(struct Qdisc *sch, struct sk_buff *skb) ++{ ++ mq_dump_common(sch, skb); ++ return 0; ++} ++ ++static int cake_mq_change(struct Qdisc *sch, struct nlattr *opt, ++ struct netlink_ext_ack *extack) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static int cake_mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new, ++ struct Qdisc **old, struct netlink_ext_ack *extack) ++{ ++ NL_SET_ERR_MSG(extack, "can't replace cake_mq sub-qdiscs"); ++ return -EOPNOTSUPP; ++} ++ ++static const struct Qdisc_class_ops cake_mq_class_ops = { ++ .select_queue = mq_select_queue, ++ .graft = cake_mq_graft, ++ .leaf = mq_leaf, ++ .find = mq_find, ++ .walk = mq_walk, ++ .dump = mq_dump_class, ++ .dump_stats = mq_dump_class_stats, ++}; ++ ++static struct Qdisc_ops cake_mq_qdisc_ops __read_mostly = { ++ .cl_ops = &cake_mq_class_ops, ++ .id = "cake_mq", ++ .priv_size = sizeof(struct cake_mq_sched), ++ .init = cake_mq_init, ++ .destroy = cake_mq_destroy, ++ .attach = mq_attach, ++ .change = cake_mq_change, ++ .change_real_num_tx = mq_change_real_num_tx, ++ .dump = cake_mq_dump, ++ .owner = THIS_MODULE, ++}; ++MODULE_ALIAS_NET_SCH("cake_mq"); ++ + static int __init cake_module_init(void) + { +- return register_qdisc(&cake_qdisc_ops); ++ int ret; ++ ++ ret = register_qdisc(&cake_qdisc_ops); ++ if (ret) ++ return ret; ++ ++ ret = register_qdisc(&cake_mq_qdisc_ops); ++ if (ret) ++ unregister_qdisc(&cake_qdisc_ops); ++ ++ return ret; + } + + static void __exit cake_module_exit(void) + { + unregister_qdisc(&cake_qdisc_ops); ++ unregister_qdisc(&cake_mq_qdisc_ops); + } + + module_init(cake_module_init) +@@ -3169,3 +3245,4 @@ module_exit(cake_module_exit) + MODULE_AUTHOR("Jonathan Morton"); + MODULE_LICENSE("Dual BSD/GPL"); + MODULE_DESCRIPTION("The CAKE shaper."); ++MODULE_IMPORT_NS(NET_SCHED_INTERNAL); diff --git a/target/linux/generic/backport-6.12/700-04-v7.0-net-sched-sch_cake-Share-config-across-cake_mq-sub-q.patch b/target/linux/generic/backport-6.12/700-04-v7.0-net-sched-sch_cake-Share-config-across-cake_mq-sub-q.patch new file mode 100644 index 00000000000..e481ef9c0e5 --- /dev/null +++ b/target/linux/generic/backport-6.12/700-04-v7.0-net-sched-sch_cake-Share-config-across-cake_mq-sub-q.patch @@ -0,0 +1,318 @@ +From 68e71714d1e02ca388ea99defbe76061b7dd09a3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= +Date: Fri, 9 Jan 2026 14:15:33 +0100 +Subject: [PATCH 4/7] net/sched: sch_cake: Share config across cake_mq + sub-qdiscs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This adds support for configuring the cake_mq instance directly, sharing +the config across the cake sub-qdiscs. + +Signed-off-by: Toke Høiland-Jørgensen +Link: https://patch.msgid.link/20260109-mq-cake-sub-qdisc-v8-4-8d613fece5d8@redhat.com +Signed-off-by: Paolo Abeni +--- + net/sched/sch_cake.c | 173 +++++++++++++++++++++++++++++++++---------- + 1 file changed, 133 insertions(+), 40 deletions(-) + +--- a/net/sched/sch_cake.c ++++ b/net/sched/sch_cake.c +@@ -212,6 +212,7 @@ struct cake_sched_config { + u8 flow_mode; + u8 atm_mode; + u8 ack_filter; ++ u8 is_shared; + }; + + struct cake_sched_data { +@@ -2586,14 +2587,12 @@ static void cake_reconfigure(struct Qdis + q->buffer_config_limit)); + } + +-static int cake_change(struct Qdisc *sch, struct nlattr *opt, +- struct netlink_ext_ack *extack) ++static int cake_config_change(struct cake_sched_config *q, struct nlattr *opt, ++ struct netlink_ext_ack *extack, bool *overhead_changed) + { +- struct cake_sched_data *qd = qdisc_priv(sch); +- struct cake_sched_config *q = qd->config; + struct nlattr *tb[TCA_CAKE_MAX + 1]; +- u16 rate_flags; +- u8 flow_mode; ++ u16 rate_flags = q->rate_flags; ++ u8 flow_mode = q->flow_mode; + int err; + + err = nla_parse_nested_deprecated(tb, TCA_CAKE_MAX, opt, cake_policy, +@@ -2601,7 +2600,6 @@ static int cake_change(struct Qdisc *sch + if (err < 0) + return err; + +- flow_mode = q->flow_mode; + if (tb[TCA_CAKE_NAT]) { + #if IS_ENABLED(CONFIG_NF_CONNTRACK) + flow_mode &= ~CAKE_FLOW_NAT_FLAG; +@@ -2614,6 +2612,19 @@ static int cake_change(struct Qdisc *sch + #endif + } + ++ if (tb[TCA_CAKE_AUTORATE]) { ++ if (!!nla_get_u32(tb[TCA_CAKE_AUTORATE])) { ++ if (q->is_shared) { ++ NL_SET_ERR_MSG_ATTR(extack, tb[TCA_CAKE_AUTORATE], ++ "Can't use autorate-ingress with cake_mq"); ++ return -EOPNOTSUPP; ++ } ++ rate_flags |= CAKE_FLAG_AUTORATE_INGRESS; ++ } else { ++ rate_flags &= ~CAKE_FLAG_AUTORATE_INGRESS; ++ } ++ } ++ + if (tb[TCA_CAKE_BASE_RATE64]) + WRITE_ONCE(q->rate_bps, + nla_get_u64(tb[TCA_CAKE_BASE_RATE64])); +@@ -2622,7 +2633,6 @@ static int cake_change(struct Qdisc *sch + WRITE_ONCE(q->tin_mode, + nla_get_u32(tb[TCA_CAKE_DIFFSERV_MODE])); + +- rate_flags = q->rate_flags; + if (tb[TCA_CAKE_WASH]) { + if (!!nla_get_u32(tb[TCA_CAKE_WASH])) + rate_flags |= CAKE_FLAG_WASH; +@@ -2643,20 +2653,12 @@ static int cake_change(struct Qdisc *sch + WRITE_ONCE(q->rate_overhead, + nla_get_s32(tb[TCA_CAKE_OVERHEAD])); + rate_flags |= CAKE_FLAG_OVERHEAD; +- +- qd->max_netlen = 0; +- qd->max_adjlen = 0; +- qd->min_netlen = ~0; +- qd->min_adjlen = ~0; ++ *overhead_changed = true; + } + + if (tb[TCA_CAKE_RAW]) { + rate_flags &= ~CAKE_FLAG_OVERHEAD; +- +- qd->max_netlen = 0; +- qd->max_adjlen = 0; +- qd->min_netlen = ~0; +- qd->min_adjlen = ~0; ++ *overhead_changed = true; + } + + if (tb[TCA_CAKE_MPU]) +@@ -2675,13 +2677,6 @@ static int cake_change(struct Qdisc *sch + WRITE_ONCE(q->target, max(target, 1U)); + } + +- if (tb[TCA_CAKE_AUTORATE]) { +- if (!!nla_get_u32(tb[TCA_CAKE_AUTORATE])) +- rate_flags |= CAKE_FLAG_AUTORATE_INGRESS; +- else +- rate_flags &= ~CAKE_FLAG_AUTORATE_INGRESS; +- } +- + if (tb[TCA_CAKE_INGRESS]) { + if (!!nla_get_u32(tb[TCA_CAKE_INGRESS])) + rate_flags |= CAKE_FLAG_INGRESS; +@@ -2712,6 +2707,34 @@ static int cake_change(struct Qdisc *sch + + WRITE_ONCE(q->rate_flags, rate_flags); + WRITE_ONCE(q->flow_mode, flow_mode); ++ ++ return 0; ++} ++ ++static int cake_change(struct Qdisc *sch, struct nlattr *opt, ++ struct netlink_ext_ack *extack) ++{ ++ struct cake_sched_data *qd = qdisc_priv(sch); ++ struct cake_sched_config *q = qd->config; ++ bool overhead_changed = false; ++ int ret; ++ ++ if (q->is_shared) { ++ NL_SET_ERR_MSG(extack, "can't reconfigure cake_mq sub-qdiscs"); ++ return -EOPNOTSUPP; ++ } ++ ++ ret = cake_config_change(q, opt, extack, &overhead_changed); ++ if (ret) ++ return ret; ++ ++ if (overhead_changed) { ++ qd->max_netlen = 0; ++ qd->max_adjlen = 0; ++ qd->min_netlen = ~0; ++ qd->min_adjlen = ~0; ++ } ++ + if (qd->tins) { + sch_tree_lock(sch); + cake_reconfigure(sch); +@@ -2728,7 +2751,23 @@ static void cake_destroy(struct Qdisc *s + qdisc_watchdog_cancel(&q->watchdog); + tcf_block_put(q->block); + kvfree(q->tins); +- kvfree(q->config); ++ if (q->config && !q->config->is_shared) ++ kvfree(q->config); ++} ++ ++static void cake_config_init(struct cake_sched_config *q, bool is_shared) ++{ ++ q->tin_mode = CAKE_DIFFSERV_DIFFSERV3; ++ q->flow_mode = CAKE_FLOW_TRIPLE; ++ ++ q->rate_bps = 0; /* unlimited by default */ ++ ++ q->interval = 100000; /* 100ms default */ ++ q->target = 5000; /* 5ms: codel RFC argues ++ * for 5 to 10% of interval ++ */ ++ q->rate_flags |= CAKE_FLAG_SPLIT_GSO; ++ q->is_shared = is_shared; + } + + static int cake_init(struct Qdisc *sch, struct nlattr *opt, +@@ -2742,17 +2781,9 @@ static int cake_init(struct Qdisc *sch, + if (!q) + return -ENOMEM; + +- sch->limit = 10240; +- q->tin_mode = CAKE_DIFFSERV_DIFFSERV3; +- q->flow_mode = CAKE_FLOW_TRIPLE; ++ cake_config_init(q, false); + +- q->rate_bps = 0; /* unlimited by default */ +- +- q->interval = 100000; /* 100ms default */ +- q->target = 5000; /* 5ms: codel RFC argues +- * for 5 to 10% of interval +- */ +- q->rate_flags |= CAKE_FLAG_SPLIT_GSO; ++ sch->limit = 10240; + qd->cur_tin = 0; + qd->cur_flow = 0; + qd->config = q; +@@ -2815,10 +2846,21 @@ err: + return err; + } + +-static int cake_dump(struct Qdisc *sch, struct sk_buff *skb) ++static void cake_config_replace(struct Qdisc *sch, struct cake_sched_config *cfg) + { + struct cake_sched_data *qd = qdisc_priv(sch); + struct cake_sched_config *q = qd->config; ++ ++ qd->config = cfg; ++ ++ if (!q->is_shared) ++ kvfree(q); ++ ++ cake_reconfigure(sch); ++} ++ ++static int cake_config_dump(struct cake_sched_config *q, struct sk_buff *skb) ++{ + struct nlattr *opts; + u16 rate_flags; + u8 flow_mode; +@@ -2894,6 +2936,13 @@ nla_put_failure: + return -1; + } + ++static int cake_dump(struct Qdisc *sch, struct sk_buff *skb) ++{ ++ struct cake_sched_data *qd = qdisc_priv(sch); ++ ++ return cake_config_dump(qd->config, skb); ++} ++ + static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d) + { + struct nlattr *stats = nla_nest_start_noflag(d->skb, TCA_STATS_APP); +@@ -3157,6 +3206,7 @@ MODULE_ALIAS_NET_SCH("cake"); + + struct cake_mq_sched { + struct mq_sched mq_priv; /* must be first */ ++ struct cake_sched_config cake_config; + }; + + static void cake_mq_destroy(struct Qdisc *sch) +@@ -3167,25 +3217,68 @@ static void cake_mq_destroy(struct Qdisc + static int cake_mq_init(struct Qdisc *sch, struct nlattr *opt, + struct netlink_ext_ack *extack) + { +- int ret; ++ struct cake_mq_sched *priv = qdisc_priv(sch); ++ struct net_device *dev = qdisc_dev(sch); ++ int ret, ntx; ++ bool _unused; ++ ++ cake_config_init(&priv->cake_config, true); ++ if (opt) { ++ ret = cake_config_change(&priv->cake_config, opt, extack, &_unused); ++ if (ret) ++ return ret; ++ } + + ret = mq_init_common(sch, opt, extack, &cake_qdisc_ops); + if (ret) + return ret; + ++ for (ntx = 0; ntx < dev->num_tx_queues; ntx++) ++ cake_config_replace(priv->mq_priv.qdiscs[ntx], &priv->cake_config); ++ + return 0; + } + + static int cake_mq_dump(struct Qdisc *sch, struct sk_buff *skb) + { ++ struct cake_mq_sched *priv = qdisc_priv(sch); ++ + mq_dump_common(sch, skb); +- return 0; ++ return cake_config_dump(&priv->cake_config, skb); + } + + static int cake_mq_change(struct Qdisc *sch, struct nlattr *opt, + struct netlink_ext_ack *extack) + { +- return -EOPNOTSUPP; ++ struct cake_mq_sched *priv = qdisc_priv(sch); ++ struct net_device *dev = qdisc_dev(sch); ++ bool overhead_changed = false; ++ unsigned int ntx; ++ int ret; ++ ++ ret = cake_config_change(&priv->cake_config, opt, extack, &overhead_changed); ++ if (ret) ++ return ret; ++ ++ for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { ++ struct Qdisc *chld = rtnl_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); ++ struct cake_sched_data *qd = qdisc_priv(chld); ++ ++ if (overhead_changed) { ++ qd->max_netlen = 0; ++ qd->max_adjlen = 0; ++ qd->min_netlen = ~0; ++ qd->min_adjlen = ~0; ++ } ++ ++ if (qd->tins) { ++ sch_tree_lock(chld); ++ cake_reconfigure(chld); ++ sch_tree_unlock(chld); ++ } ++ } ++ ++ return 0; + } + + static int cake_mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new, diff --git a/target/linux/generic/backport-6.12/700-05-v7.0-net-sched-sch_cake-share-shaper-state-across-sub-ins.patch b/target/linux/generic/backport-6.12/700-05-v7.0-net-sched-sch_cake-share-shaper-state-across-sub-ins.patch new file mode 100644 index 00000000000..62bf38d35da --- /dev/null +++ b/target/linux/generic/backport-6.12/700-05-v7.0-net-sched-sch_cake-share-shaper-state-across-sub-ins.patch @@ -0,0 +1,175 @@ +From 090dde0780266eb80126fa970870bafdb4e937c3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20K=C3=B6ppeler?= +Date: Fri, 9 Jan 2026 14:15:34 +0100 +Subject: [PATCH 5/7] net/sched: sch_cake: share shaper state across + sub-instances of cake_mq +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This commit adds shared shaper state across the cake instances beneath a +cake_mq qdisc. It works by periodically tracking the number of active +instances, and scaling the configured rate by the number of active +queues. + +The scan is lockless and simply reads the qlen and the last_active state +variable of each of the instances configured beneath the parent cake_mq +instance. Locking is not required since the values are only updated by +the owning instance, and eventual consistency is sufficient for the +purpose of estimating the number of active queues. + +The interval for scanning the number of active queues is set to 200 us. +We found this to be a good tradeoff between overhead and response time. +For a detailed analysis of this aspect see the Netdevconf talk: + +https://netdevconf.info/0x19/docs/netdev-0x19-paper16-talk-paper.pdf + +Reviewed-by: Jamal Hadi Salim +Signed-off-by: Jonas Köppeler +Signed-off-by: Toke Høiland-Jørgensen +Link: https://patch.msgid.link/20260109-mq-cake-sub-qdisc-v8-5-8d613fece5d8@redhat.com +Signed-off-by: Paolo Abeni +--- + Documentation/netlink/specs/tc.yaml | 3 ++ + include/uapi/linux/pkt_sched.h | 1 + + net/sched/sch_cake.c | 51 +++++++++++++++++++++++++++++ + 3 files changed, 55 insertions(+) + +--- a/Documentation/netlink/specs/tc.yaml ++++ b/Documentation/netlink/specs/tc.yaml +@@ -2161,6 +2161,9 @@ attribute-sets: + - + name: blue-timer-us + type: s32 ++ - ++ name: active-queues ++ type: u32 + - + name: tc-cake-tin-stats-attrs + attributes: +--- a/include/uapi/linux/pkt_sched.h ++++ b/include/uapi/linux/pkt_sched.h +@@ -1034,6 +1034,7 @@ enum { + TCA_CAKE_STATS_DROP_NEXT_US, + TCA_CAKE_STATS_P_DROP, + TCA_CAKE_STATS_BLUE_TIMER_US, ++ TCA_CAKE_STATS_ACTIVE_QUEUES, + __TCA_CAKE_STATS_MAX + }; + #define TCA_CAKE_STATS_MAX (__TCA_CAKE_STATS_MAX - 1) +--- a/net/sched/sch_cake.c ++++ b/net/sched/sch_cake.c +@@ -202,6 +202,7 @@ struct cake_sched_config { + u64 rate_bps; + u64 interval; + u64 target; ++ u64 sync_time; + u32 buffer_config_limit; + u32 fwmark_mask; + u16 fwmark_shft; +@@ -258,6 +259,11 @@ struct cake_sched_data { + u16 max_adjlen; + u16 min_netlen; + u16 min_adjlen; ++ ++ /* mq sync state */ ++ u64 last_checked_active; ++ u64 last_active; ++ u32 active_queues; + }; + + enum { +@@ -384,6 +390,8 @@ static const u32 inv_sqrt_cache[REC_INV_ + 1239850263, 1191209601, 1147878294, 1108955788 + }; + ++static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu, ++ u64 target_ns, u64 rtt_est_ns); + /* http://en.wikipedia.org/wiki/Methods_of_computing_square_roots + * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2) + * +@@ -2003,6 +2011,40 @@ static struct sk_buff *cake_dequeue(stru + u64 delay; + u32 len; + ++ if (q->config->is_shared && now - q->last_checked_active >= q->config->sync_time) { ++ struct net_device *dev = qdisc_dev(sch); ++ struct cake_sched_data *other_priv; ++ u64 new_rate = q->config->rate_bps; ++ u64 other_qlen, other_last_active; ++ struct Qdisc *other_sch; ++ u32 num_active_qs = 1; ++ unsigned int ntx; ++ ++ for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { ++ other_sch = rcu_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); ++ other_priv = qdisc_priv(other_sch); ++ ++ if (other_priv == q) ++ continue; ++ ++ other_qlen = READ_ONCE(other_sch->q.qlen); ++ other_last_active = READ_ONCE(other_priv->last_active); ++ ++ if (other_qlen || other_last_active > q->last_checked_active) ++ num_active_qs++; ++ } ++ ++ if (num_active_qs > 1) ++ new_rate = div64_u64(q->config->rate_bps, num_active_qs); ++ ++ /* mtu = 0 is used to only update the rate and not mess with cobalt params */ ++ cake_set_rate(b, new_rate, 0, 0, 0); ++ q->last_checked_active = now; ++ q->active_queues = num_active_qs; ++ q->rate_ns = b->tin_rate_ns; ++ q->rate_shft = b->tin_rate_shft; ++ } ++ + begin: + if (!sch->q.qlen) + return NULL; +@@ -2202,6 +2244,7 @@ retry: + + b->tin_ecn_mark += !!flow->cvars.ecn_marked; + qdisc_bstats_update(sch, skb); ++ WRITE_ONCE(q->last_active, now); + + /* collect delay stats */ + delay = ktime_to_ns(ktime_sub(now, cobalt_get_enqueue_time(skb))); +@@ -2302,6 +2345,9 @@ static void cake_set_rate(struct cake_ti + b->tin_rate_ns = rate_ns; + b->tin_rate_shft = rate_shft; + ++ if (mtu == 0) ++ return; ++ + byte_target_ns = (byte_target * rate_ns) >> rate_shft; + + b->cparams.target = max((byte_target_ns * 3) / 2, target_ns); +@@ -2768,6 +2814,7 @@ static void cake_config_init(struct cake + */ + q->rate_flags |= CAKE_FLAG_SPLIT_GSO; + q->is_shared = is_shared; ++ q->sync_time = 200 * NSEC_PER_USEC; + } + + static int cake_init(struct Qdisc *sch, struct nlattr *opt, +@@ -2839,6 +2886,9 @@ static int cake_init(struct Qdisc *sch, + qd->avg_peak_bandwidth = q->rate_bps; + qd->min_netlen = ~0; + qd->min_adjlen = ~0; ++ qd->active_queues = 0; ++ qd->last_checked_active = 0; ++ + return 0; + err: + kvfree(qd->config); +@@ -2971,6 +3021,7 @@ static int cake_dump_stats(struct Qdisc + PUT_STAT_U32(MAX_ADJLEN, q->max_adjlen); + PUT_STAT_U32(MIN_NETLEN, q->min_netlen); + PUT_STAT_U32(MIN_ADJLEN, q->min_adjlen); ++ PUT_STAT_U32(ACTIVE_QUEUES, q->active_queues); + + #undef PUT_STAT_U32 + #undef PUT_STAT_U64 diff --git a/target/linux/generic/backport-6.12/700-06-v7.0-selftests-tc-testing-add-selftests-for-cake_mq-qdisc.patch b/target/linux/generic/backport-6.12/700-06-v7.0-selftests-tc-testing-add-selftests-for-cake_mq-qdisc.patch new file mode 100644 index 00000000000..c012a158840 --- /dev/null +++ b/target/linux/generic/backport-6.12/700-06-v7.0-selftests-tc-testing-add-selftests-for-cake_mq-qdisc.patch @@ -0,0 +1,606 @@ +From e5e7c7c6a027ce2f001fc60dc388a560c01427fd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20K=C3=B6ppeler?= +Date: Fri, 9 Jan 2026 14:15:35 +0100 +Subject: [PATCH 6/7] selftests/tc-testing: add selftests for cake_mq qdisc +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Test 684b: Create CAKE_MQ with default setting (4 queues) +Test 7ee8: Create CAKE_MQ with bandwidth limit (4 queues) +Test 1f87: Create CAKE_MQ with rtt time (4 queues) +Test e9cf: Create CAKE_MQ with besteffort flag (4 queues) +Test 7c05: Create CAKE_MQ with diffserv8 flag (4 queues) +Test 5a77: Create CAKE_MQ with diffserv4 flag (4 queues) +Test 8f7a: Create CAKE_MQ with flowblind flag (4 queues) +Test 7ef7: Create CAKE_MQ with dsthost and nat flag (4 queues) +Test 2e4d: Create CAKE_MQ with wash flag (4 queues) +Test b3e6: Create CAKE_MQ with flowblind and no-split-gso flag (4 queues) +Test 62cd: Create CAKE_MQ with dual-srchost and ack-filter flag (4 queues) +Test 0df3: Create CAKE_MQ with dual-dsthost and ack-filter-aggressive flag (4 queues) +Test 9a75: Create CAKE_MQ with memlimit and ptm flag (4 queues) +Test cdef: Create CAKE_MQ with fwmark and atm flag (4 queues) +Test 93dd: Create CAKE_MQ with overhead 0 and mpu (4 queues) +Test 1475: Create CAKE_MQ with conservative and ingress flag (4 queues) +Test 7bf1: Delete CAKE_MQ with conservative and ingress flag (4 queues) +Test ee55: Replace CAKE_MQ with mpu (4 queues) +Test 6df9: Change CAKE_MQ with mpu (4 queues) +Test 67e2: Show CAKE_MQ class (4 queues) +Test 2de4: Change bandwidth of CAKE_MQ (4 queues) +Test 5f62: Fail to create CAKE_MQ with autorate-ingress flag (4 queues) +Test 038e: Fail to change setting of sub-qdisc under CAKE_MQ +Test 7bdc: Fail to replace sub-qdisc under CAKE_MQ +Test 18e0: Fail to install CAKE_MQ on single queue device + +Reviewed-by: Victor Nogueira +Signed-off-by: Jonas Köppeler +Signed-off-by: Toke Høiland-Jørgensen +Link: https://patch.msgid.link/20260109-mq-cake-sub-qdisc-v8-6-8d613fece5d8@redhat.com +Signed-off-by: Paolo Abeni +--- + .../tc-testing/tc-tests/qdiscs/cake_mq.json | 559 ++++++++++++++++++ + 1 file changed, 559 insertions(+) + create mode 100644 tools/testing/selftests/tc-testing/tc-tests/qdiscs/cake_mq.json + +--- /dev/null ++++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/cake_mq.json +@@ -0,0 +1,559 @@ ++[ ++ { ++ "id": "684b", ++ "name": "Create CAKE_MQ with default setting (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device || true", ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "7ee8", ++ "name": "Create CAKE_MQ with bandwidth limit (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq bandwidth 1000", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth 1Kbit diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "1f87", ++ "name": "Create CAKE_MQ with rtt time (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq rtt 200", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 200us raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "e9cf", ++ "name": "Create CAKE_MQ with besteffort flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq besteffort", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited besteffort triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "7c05", ++ "name": "Create CAKE_MQ with diffserv8 flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq diffserv8", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv8 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "5a77", ++ "name": "Create CAKE_MQ with diffserv4 flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq diffserv4", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv4 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "8f7a", ++ "name": "Create CAKE_MQ with flowblind flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq flowblind", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 flowblind nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "7ef7", ++ "name": "Create CAKE_MQ with dsthost and nat flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq dsthost nat", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 dsthost nat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "2e4d", ++ "name": "Create CAKE_MQ with wash flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq hosts wash", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 hosts nonat wash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "b3e6", ++ "name": "Create CAKE_MQ with flowblind and no-split-gso flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq flowblind no-split-gso", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 flowblind nonat nowash no-ack-filter no-split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "62cd", ++ "name": "Create CAKE_MQ with dual-srchost and ack-filter flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq dual-srchost ack-filter", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 dual-srchost nonat nowash ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "0df3", ++ "name": "Create CAKE_MQ with dual-dsthost and ack-filter-aggressive flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq dual-dsthost ack-filter-aggressive", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 dual-dsthost nonat nowash ack-filter-aggressive split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "9a75", ++ "name": "Create CAKE_MQ with memlimit and ptm flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq memlimit 10000 ptm", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw ptm overhead 0 memlimit 10000b ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "cdef", ++ "name": "Create CAKE_MQ with fwmark and atm flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq fwmark 8 atm", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw atm overhead 0 fwmark 0x8 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "93dd", ++ "name": "Create CAKE_MQ with overhead 0 and mpu (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq overhead 128 mpu 256", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms noatm overhead 128 mpu 256 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "1475", ++ "name": "Create CAKE_MQ with conservative and ingress flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq conservative ingress", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash ingress no-ack-filter split-gso rtt 100ms atm overhead 48 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "7bf1", ++ "name": "Delete CAKE_MQ with conservative and ingress flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device", ++ "$TC qdisc add dev $ETH handle 1: root cake_mq conservative ingress" ++ ], ++ "cmdUnderTest": "$TC qdisc del dev $ETH handle 1: root", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash ingress no-ack-filter split-gso rtt 100ms atm overhead 48 ", ++ "matchCount": "0", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "ee55", ++ "name": "Replace CAKE_MQ with mpu (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device", ++ "$TC qdisc add dev $ETH handle 1: root cake_mq overhead 128 mpu 256" ++ ], ++ "cmdUnderTest": "$TC qdisc replace dev $ETH handle 1: root cake_mq mpu 128", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms noatm overhead 128 mpu 128 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "6df9", ++ "name": "Change CAKE_MQ with mpu (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device", ++ "$TC qdisc add dev $ETH handle 1: root cake_mq overhead 128 mpu 256" ++ ], ++ "cmdUnderTest": "$TC qdisc change dev $ETH handle 1: root cake_mq mpu 128", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms noatm overhead 128 mpu 128 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "67e2", ++ "name": "Show CAKE_MQ class (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq", ++ "expExitCode": "0", ++ "verifyCmd": "$TC class show dev $ETH", ++ "matchPattern": "class cake_mq", ++ "matchCount": "4", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "2de4", ++ "name": "Change bandwidth of CAKE_MQ (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device", ++ "$TC qdisc add dev $ETH handle 1: root cake_mq" ++ ], ++ "cmdUnderTest": "$TC qdisc replace dev $ETH handle 1: root cake_mq bandwidth 1000", ++ "expExitCode": "0", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth 1Kbit diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "5f62", ++ "name": "Fail to create CAKE_MQ with autorate-ingress flag (4 queues)", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq autorate-ingress", ++ "expExitCode": "2", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited autorate-ingress diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "0", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "038e", ++ "name": "Fail to change setting of sub-qdisc under CAKE_MQ", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device", ++ "$TC qdisc add dev $ETH handle 1: root cake_mq" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH parent 1:1 cake besteffort flows", ++ "expExitCode": "2", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "7bdc", ++ "name": "Fail to replace sub-qdisc under CAKE_MQ", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 4\" > /sys/bus/netdevsim/new_device", ++ "$TC qdisc add dev $ETH handle 1: root cake_mq" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH parent 1:1 fq", ++ "expExitCode": "2", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "5", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ }, ++ { ++ "id": "18e0", ++ "name": "Fail to install CAKE_MQ on single queue device", ++ "category": [ ++ "qdisc", ++ "cake_mq" ++ ], ++ "plugins": { ++ "requires": "nsPlugin" ++ }, ++ "setup": [ ++ "echo \"1 1 1\" > /sys/bus/netdevsim/new_device" ++ ], ++ "cmdUnderTest": "$TC qdisc add dev $ETH handle 1: root cake_mq", ++ "expExitCode": "2", ++ "verifyCmd": "$TC qdisc show dev $ETH", ++ "matchPattern": "qdisc (cake_mq 1: root|cake 0: parent 1:[1-4]) bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0 ", ++ "matchCount": "0", ++ "teardown": [ ++ "echo \"1\" > /sys/bus/netdevsim/del_device" ++ ] ++ } ++] diff --git a/target/linux/generic/backport-6.12/700-07-v7.0-net-sched-cake-avoid-separate-allocation-of-struct-c.patch b/target/linux/generic/backport-6.12/700-07-v7.0-net-sched-cake-avoid-separate-allocation-of-struct-c.patch new file mode 100644 index 00000000000..1ba4cd3bc48 --- /dev/null +++ b/target/linux/generic/backport-6.12/700-07-v7.0-net-sched-cake-avoid-separate-allocation-of-struct-c.patch @@ -0,0 +1,112 @@ +From 060969bc048d2f85149b7f1a8bbb56e6b1434cae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= +Date: Tue, 13 Jan 2026 15:31:56 +0100 +Subject: [PATCH 7/7] net/sched: cake: avoid separate allocation of struct + cake_sched_config +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Paolo pointed out that we can avoid separately allocating struct +cake_sched_config even in the non-mq case, by embedding it into struct +cake_sched_data. This reduces the complexity of the logic that swaps the +pointers and frees the old value, at the cost of adding 56 bytes to the +latter. Since cake_sched_data is already almost 17k bytes, this seems +like a reasonable tradeoff. + +Suggested-by: Paolo Abeni +Signed-off-by: Toke Høiland-Jørgensen +Fixes: bc0ce2bad36c ("net/sched: sch_cake: Factor out config variables into separate struct") +Link: https://patch.msgid.link/20260113143157.2581680-1-toke@redhat.com +Signed-off-by: Jakub Kicinski +--- + net/sched/sch_cake.c | 29 ++++++----------------------- + 1 file changed, 6 insertions(+), 23 deletions(-) + +--- a/net/sched/sch_cake.c ++++ b/net/sched/sch_cake.c +@@ -221,6 +221,7 @@ struct cake_sched_data { + struct tcf_block *block; + struct cake_tin_data *tins; + struct cake_sched_config *config; ++ struct cake_sched_config initial_config; + + struct cake_heap_entry overflow_heap[CAKE_QUEUES * CAKE_MAX_TINS]; + +@@ -2797,8 +2798,6 @@ static void cake_destroy(struct Qdisc *s + qdisc_watchdog_cancel(&q->watchdog); + tcf_block_put(q->block); + kvfree(q->tins); +- if (q->config && !q->config->is_shared) +- kvfree(q->config); + } + + static void cake_config_init(struct cake_sched_config *q, bool is_shared) +@@ -2821,13 +2820,9 @@ static int cake_init(struct Qdisc *sch, + struct netlink_ext_ack *extack) + { + struct cake_sched_data *qd = qdisc_priv(sch); +- struct cake_sched_config *q; ++ struct cake_sched_config *q = &qd->initial_config; + int i, j, err; + +- q = kzalloc(sizeof(*q), GFP_KERNEL); +- if (!q) +- return -ENOMEM; +- + cake_config_init(q, false); + + sch->limit = 10240; +@@ -2839,14 +2834,13 @@ static int cake_init(struct Qdisc *sch, + + if (opt) { + err = cake_change(sch, opt, extack); +- + if (err) +- goto err; ++ return err; + } + + err = tcf_block_get(&qd->block, &qd->filter_list, sch, extack); + if (err) +- goto err; ++ return err; + + quantum_div[0] = ~0; + for (i = 1; i <= CAKE_QUEUES; i++) +@@ -2854,10 +2848,8 @@ static int cake_init(struct Qdisc *sch, + + qd->tins = kvcalloc(CAKE_MAX_TINS, sizeof(struct cake_tin_data), + GFP_KERNEL); +- if (!qd->tins) { +- err = -ENOMEM; +- goto err; +- } ++ if (!qd->tins) ++ return -ENOMEM; + + for (i = 0; i < CAKE_MAX_TINS; i++) { + struct cake_tin_data *b = qd->tins + i; +@@ -2890,22 +2882,13 @@ static int cake_init(struct Qdisc *sch, + qd->last_checked_active = 0; + + return 0; +-err: +- kvfree(qd->config); +- qd->config = NULL; +- return err; + } + + static void cake_config_replace(struct Qdisc *sch, struct cake_sched_config *cfg) + { + struct cake_sched_data *qd = qdisc_priv(sch); +- struct cake_sched_config *q = qd->config; + + qd->config = cfg; +- +- if (!q->is_shared) +- kvfree(q); +- + cake_reconfigure(sch); + } + diff --git a/target/linux/generic/backport-6.12/701-01-v7.0-net-sched-sch_cake-avoid-sync-overhead-when-unlimite.patch b/target/linux/generic/backport-6.12/701-01-v7.0-net-sched-sch_cake-avoid-sync-overhead-when-unlimite.patch new file mode 100644 index 00000000000..60a003c1de4 --- /dev/null +++ b/target/linux/generic/backport-6.12/701-01-v7.0-net-sched-sch_cake-avoid-sync-overhead-when-unlimite.patch @@ -0,0 +1,32 @@ +From 0b3cd139be565b85f4a3579e376152b9def6256a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20K=C3=B6ppeler?= +Date: Thu, 26 Feb 2026 12:40:15 +0100 +Subject: [PATCH] net/sched: sch_cake: avoid sync overhead when unlimited +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Skip inter-instance sync when no rate limit is configured, as it serves +no purpose and only adds overhead. + +Fixes: 1bddd758bac2 ("net/sched: sch_cake: share shaper state across sub-instances of cake_mq") +Signed-off-by: Jonas Köppeler +Acked-by: Toke Høiland-Jørgensen +Link: https://patch.msgid.link/20260226-cake-mq-skip-sync-bandwidth-unlimited-v1-1-01830bb4db87@tu-berlin.de +Signed-off-by: Jakub Kicinski +--- + net/sched/sch_cake.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/net/sched/sch_cake.c ++++ b/net/sched/sch_cake.c +@@ -2012,7 +2012,8 @@ static struct sk_buff *cake_dequeue(stru + u64 delay; + u32 len; + +- if (q->config->is_shared && now - q->last_checked_active >= q->config->sync_time) { ++ if (q->config->is_shared && q->rate_ns && ++ now - q->last_checked_active >= q->config->sync_time) { + struct net_device *dev = qdisc_dev(sch); + struct cake_sched_data *other_priv; + u64 new_rate = q->config->rate_bps; diff --git a/target/linux/generic/backport-6.12/701-02-v7.0-net-sched-sch_cake-fixup-cake_mq-rate-adjustment-for.patch b/target/linux/generic/backport-6.12/701-02-v7.0-net-sched-sch_cake-fixup-cake_mq-rate-adjustment-for.patch new file mode 100644 index 00000000000..357f6b67360 --- /dev/null +++ b/target/linux/generic/backport-6.12/701-02-v7.0-net-sched-sch_cake-fixup-cake_mq-rate-adjustment-for.patch @@ -0,0 +1,187 @@ +From 15c2715a52645fd8e6e18b7abc3449292d118c7c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20K=C3=B6ppeler?= +Date: Thu, 26 Feb 2026 12:40:16 +0100 +Subject: [PATCH] net/sched: sch_cake: fixup cake_mq rate adjustment for + diffserv config +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +cake_mq's rate adjustment during the sync periods did not adjust the +rates for every tin in a diffserv config. This lead to inconsistencies +of rates between the tins. Fix this by setting the rates for all tins +during synchronization. + +Fixes: 1bddd758bac2 ("net/sched: sch_cake: share shaper state across sub-instances of cake_mq") +Signed-off-by: Jonas Köppeler +Acked-by: Toke Høiland-Jørgensen +Link: https://patch.msgid.link/20260226-cake-mq-skip-sync-bandwidth-unlimited-v1-2-01830bb4db87@tu-berlin.de +Signed-off-by: Jakub Kicinski +--- + net/sched/sch_cake.c | 50 ++++++++++++++++++++------------------------ + 1 file changed, 23 insertions(+), 27 deletions(-) + +--- a/net/sched/sch_cake.c ++++ b/net/sched/sch_cake.c +@@ -391,8 +391,8 @@ static const u32 inv_sqrt_cache[REC_INV_ + 1239850263, 1191209601, 1147878294, 1108955788 + }; + +-static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu, +- u64 target_ns, u64 rtt_est_ns); ++static void cake_configure_rates(struct Qdisc *sch, u64 rate, bool rate_adjust); ++ + /* http://en.wikipedia.org/wiki/Methods_of_computing_square_roots + * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2) + * +@@ -2039,12 +2039,9 @@ static struct sk_buff *cake_dequeue(stru + if (num_active_qs > 1) + new_rate = div64_u64(q->config->rate_bps, num_active_qs); + +- /* mtu = 0 is used to only update the rate and not mess with cobalt params */ +- cake_set_rate(b, new_rate, 0, 0, 0); ++ cake_configure_rates(sch, new_rate, true); + q->last_checked_active = now; + q->active_queues = num_active_qs; +- q->rate_ns = b->tin_rate_ns; +- q->rate_shft = b->tin_rate_shft; + } + + begin: +@@ -2361,12 +2358,10 @@ static void cake_set_rate(struct cake_ti + b->cparams.p_dec = 1 << 20; /* 1/4096 */ + } + +-static int cake_config_besteffort(struct Qdisc *sch) ++static int cake_config_besteffort(struct Qdisc *sch, u64 rate, u32 mtu) + { + struct cake_sched_data *q = qdisc_priv(sch); + struct cake_tin_data *b = &q->tins[0]; +- u32 mtu = psched_mtu(qdisc_dev(sch)); +- u64 rate = q->config->rate_bps; + + q->tin_cnt = 1; + +@@ -2380,12 +2375,10 @@ static int cake_config_besteffort(struct + return 0; + } + +-static int cake_config_precedence(struct Qdisc *sch) ++static int cake_config_precedence(struct Qdisc *sch, u64 rate, u32 mtu) + { + /* convert high-level (user visible) parameters into internal format */ + struct cake_sched_data *q = qdisc_priv(sch); +- u32 mtu = psched_mtu(qdisc_dev(sch)); +- u64 rate = q->config->rate_bps; + u32 quantum = 256; + u32 i; + +@@ -2456,7 +2449,7 @@ static int cake_config_precedence(struct + * Total 12 traffic classes. + */ + +-static int cake_config_diffserv8(struct Qdisc *sch) ++static int cake_config_diffserv8(struct Qdisc *sch, u64 rate, u32 mtu) + { + /* Pruned list of traffic classes for typical applications: + * +@@ -2473,8 +2466,6 @@ static int cake_config_diffserv8(struct + */ + + struct cake_sched_data *q = qdisc_priv(sch); +- u32 mtu = psched_mtu(qdisc_dev(sch)); +- u64 rate = q->config->rate_bps; + u32 quantum = 256; + u32 i; + +@@ -2504,7 +2495,7 @@ static int cake_config_diffserv8(struct + return 0; + } + +-static int cake_config_diffserv4(struct Qdisc *sch) ++static int cake_config_diffserv4(struct Qdisc *sch, u64 rate, u32 mtu) + { + /* Further pruned list of traffic classes for four-class system: + * +@@ -2517,8 +2508,6 @@ static int cake_config_diffserv4(struct + */ + + struct cake_sched_data *q = qdisc_priv(sch); +- u32 mtu = psched_mtu(qdisc_dev(sch)); +- u64 rate = q->config->rate_bps; + u32 quantum = 1024; + + q->tin_cnt = 4; +@@ -2546,7 +2535,7 @@ static int cake_config_diffserv4(struct + return 0; + } + +-static int cake_config_diffserv3(struct Qdisc *sch) ++static int cake_config_diffserv3(struct Qdisc *sch, u64 rate, u32 mtu) + { + /* Simplified Diffserv structure with 3 tins. + * Latency Sensitive (CS7, CS6, EF, VA, TOS4) +@@ -2554,8 +2543,6 @@ static int cake_config_diffserv3(struct + * Low Priority (LE, CS1) + */ + struct cake_sched_data *q = qdisc_priv(sch); +- u32 mtu = psched_mtu(qdisc_dev(sch)); +- u64 rate = q->config->rate_bps; + u32 quantum = 1024; + + q->tin_cnt = 3; +@@ -2580,32 +2567,33 @@ static int cake_config_diffserv3(struct + return 0; + } + +-static void cake_reconfigure(struct Qdisc *sch) ++static void cake_configure_rates(struct Qdisc *sch, u64 rate, bool rate_adjust) + { ++ u32 mtu = likely(rate_adjust) ? 0 : psched_mtu(qdisc_dev(sch)); + struct cake_sched_data *qd = qdisc_priv(sch); + struct cake_sched_config *q = qd->config; + int c, ft; + + switch (q->tin_mode) { + case CAKE_DIFFSERV_BESTEFFORT: +- ft = cake_config_besteffort(sch); ++ ft = cake_config_besteffort(sch, rate, mtu); + break; + + case CAKE_DIFFSERV_PRECEDENCE: +- ft = cake_config_precedence(sch); ++ ft = cake_config_precedence(sch, rate, mtu); + break; + + case CAKE_DIFFSERV_DIFFSERV8: +- ft = cake_config_diffserv8(sch); ++ ft = cake_config_diffserv8(sch, rate, mtu); + break; + + case CAKE_DIFFSERV_DIFFSERV4: +- ft = cake_config_diffserv4(sch); ++ ft = cake_config_diffserv4(sch, rate, mtu); + break; + + case CAKE_DIFFSERV_DIFFSERV3: + default: +- ft = cake_config_diffserv3(sch); ++ ft = cake_config_diffserv3(sch, rate, mtu); + break; + } + +@@ -2616,6 +2604,14 @@ static void cake_reconfigure(struct Qdis + + qd->rate_ns = qd->tins[ft].tin_rate_ns; + qd->rate_shft = qd->tins[ft].tin_rate_shft; ++} ++ ++static void cake_reconfigure(struct Qdisc *sch) ++{ ++ struct cake_sched_data *qd = qdisc_priv(sch); ++ struct cake_sched_config *q = qd->config; ++ ++ cake_configure_rates(sch, qd->config->rate_bps, false); + + if (q->buffer_config_limit) { + qd->buffer_limit = q->buffer_config_limit; diff --git a/target/linux/generic/backport-6.12/710-v6.16-igc-enable-HW-vlan-tag-insertion-stripping-by-defaul.patch b/target/linux/generic/backport-6.12/710-v6.16-igc-enable-HW-vlan-tag-insertion-stripping-by-defaul.patch new file mode 100644 index 00000000000..48d03f15e7f --- /dev/null +++ b/target/linux/generic/backport-6.12/710-v6.16-igc-enable-HW-vlan-tag-insertion-stripping-by-defaul.patch @@ -0,0 +1,32 @@ +From 8cae5a0d91fea01d90ce7c1827e26934a22ca2fa Mon Sep 17 00:00:00 2001 +From: Rui Salvaterra +Date: Wed, 5 Mar 2025 11:53:56 +0000 +Subject: [PATCH] igc: enable HW vlan tag insertion/stripping by default + +This is enabled by default in other Intel drivers I've checked (e1000, e1000e, +iavf, igb and ice). Fixes an out-of-the-box performance issue when running +OpenWrt on typical mini-PCs with igc-supported Ethernet controllers and 802.1Q +VLAN configurations, as ethtool isn't part of the default packages and sane +defaults are expected. + +In my specific case, with an Intel N100-based machine with four I226-V Ethernet +controllers, my upload performance increased from under 30 Mb/s to the expected +~1 Gb/s. + +Signed-off-by: Rui Salvaterra +--- + drivers/net/ethernet/intel/igc/igc_main.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -7070,6 +7070,9 @@ static int igc_probe(struct pci_dev *pde + netdev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT | + NETDEV_XDP_ACT_XSK_ZEROCOPY; + ++ /* enable HW vlan tag insertion/stripping by default */ ++ netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; ++ + /* MTU range: 68 - 9216 */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = MAX_STD_JUMBO_FRAME_SIZE; diff --git a/target/linux/generic/backport-6.12/720-01-v6.13-net-phy-mediatek-ge-soc-Fix-coding-style.patch b/target/linux/generic/backport-6.12/720-01-v6.13-net-phy-mediatek-ge-soc-Fix-coding-style.patch new file mode 100644 index 00000000000..25bd96fa1fd --- /dev/null +++ b/target/linux/generic/backport-6.12/720-01-v6.13-net-phy-mediatek-ge-soc-Fix-coding-style.patch @@ -0,0 +1,88 @@ +From 277f96c1f3f71d6e1d3bcf650d7cd84c1442210f Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 17 Oct 2024 11:22:11 +0800 +Subject: [PATCH 01/20] net: phy: mediatek-ge-soc: Fix coding style + +This patch fixes spelling errors, re-arrange vars with +reverse Xmas tree and remove unnecessary parens in +mediatek-ge-soc.c. + +Signed-off-by: SkyLake.Huang +Reviewed-by: Simon Horman +Signed-off-by: Andrew Lunn +--- + drivers/net/phy/mediatek-ge-soc.c | 36 ++++++++++++++++--------------- + 1 file changed, 19 insertions(+), 17 deletions(-) + +--- a/drivers/net/phy/mediatek-ge-soc.c ++++ b/drivers/net/phy/mediatek-ge-soc.c +@@ -408,16 +408,17 @@ static int tx_offset_cal_efuse(struct ph + + static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf) + { +- int i; +- int bias[16] = {}; +- const int vals_9461[16] = { 7, 1, 4, 7, +- 7, 1, 4, 7, +- 7, 1, 4, 7, +- 7, 1, 4, 7 }; + const int vals_9481[16] = { 10, 6, 6, 10, + 10, 6, 6, 10, + 10, 6, 6, 10, + 10, 6, 6, 10 }; ++ const int vals_9461[16] = { 7, 1, 4, 7, ++ 7, 1, 4, 7, ++ 7, 1, 4, 7, ++ 7, 1, 4, 7 }; ++ int bias[16] = {}; ++ int i; ++ + switch (phydev->drv->phy_id) { + case MTK_GPHY_ID_MT7981: + /* We add some calibration to efuse values +@@ -1069,10 +1070,10 @@ static int start_cal(struct phy_device * + + static int mt798x_phy_calibration(struct phy_device *phydev) + { ++ struct nvmem_cell *cell; + int ret = 0; +- u32 *buf; + size_t len; +- struct nvmem_cell *cell; ++ u32 *buf; + + cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data"); + if (IS_ERR(cell)) { +@@ -1210,14 +1211,15 @@ static int mt798x_phy_led_brightness_set + return mt798x_phy_hw_led_on_set(phydev, index, (value != LED_OFF)); + } + +-static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_FULL_DUPLEX) | +- BIT(TRIGGER_NETDEV_HALF_DUPLEX) | +- BIT(TRIGGER_NETDEV_LINK) | +- BIT(TRIGGER_NETDEV_LINK_10) | +- BIT(TRIGGER_NETDEV_LINK_100) | +- BIT(TRIGGER_NETDEV_LINK_1000) | +- BIT(TRIGGER_NETDEV_RX) | +- BIT(TRIGGER_NETDEV_TX)); ++static const unsigned long supported_triggers = ++ BIT(TRIGGER_NETDEV_FULL_DUPLEX) | ++ BIT(TRIGGER_NETDEV_HALF_DUPLEX) | ++ BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX); + + static int mt798x_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) +@@ -1415,7 +1417,7 @@ static int mt7988_phy_probe_shared(struc + * LED_C and LED_D respectively. At the same time those pins are used to + * bootstrap configuration of the reference clock source (LED_A), + * DRAM DDRx16b x2/x1 (LED_B) and boot device (LED_C, LED_D). +- * In practise this is done using a LED and a resistor pulling the pin ++ * In practice this is done using a LED and a resistor pulling the pin + * either to GND or to VIO. + * The detected value at boot time is accessible at run-time using the + * TPBANK0 register located in the gpio base of the pinctrl, in order diff --git a/target/linux/generic/backport-6.12/720-02-v6.13-net-phy-mediatek-ge-soc-Shrink-line-wrapping-to-80-c.patch b/target/linux/generic/backport-6.12/720-02-v6.13-net-phy-mediatek-ge-soc-Shrink-line-wrapping-to-80-c.patch new file mode 100644 index 00000000000..fbf3a99bd74 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-02-v6.13-net-phy-mediatek-ge-soc-Shrink-line-wrapping-to-80-c.patch @@ -0,0 +1,271 @@ +From c0dc1b412f9d840c51c5ee8927bf066e15a59550 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 17 Oct 2024 11:22:12 +0800 +Subject: [PATCH 02/20] net: phy: mediatek-ge-soc: Shrink line wrapping to 80 + characters + +This patch shrinks line wrapping to 80 chars. Also, in +tx_amp_fill_result(), use FIELD_PREP() to prettify code. + +Signed-off-by: SkyLake.Huang +Reviewed-by: Simon Horman +Signed-off-by: Andrew Lunn +--- + drivers/net/phy/mediatek-ge-soc.c | 125 +++++++++++++++++++++--------- + 1 file changed, 88 insertions(+), 37 deletions(-) + +--- a/drivers/net/phy/mediatek-ge-soc.c ++++ b/drivers/net/phy/mediatek-ge-soc.c +@@ -342,7 +342,8 @@ static int cal_cycle(struct phy_device * + ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_AD_CAL_CLK, reg_val, + reg_val & MTK_PHY_DA_CAL_CLK, 500, +- ANALOG_INTERNAL_OPERATION_MAX_US, false); ++ ANALOG_INTERNAL_OPERATION_MAX_US, ++ false); + if (ret) { + phydev_err(phydev, "Calibration cycle timeout\n"); + return ret; +@@ -441,40 +442,72 @@ static int tx_amp_fill_result(struct phy + } + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, +- MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, (buf[0] + bias[0]) << 10); ++ MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, ++ buf[0] + bias[0])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, +- MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, buf[0] + bias[1]); ++ MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, ++ buf[0] + bias[1])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, +- MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, (buf[0] + bias[2]) << 10); ++ MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, ++ buf[0] + bias[2])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, +- MTK_PHY_DA_TX_I2MPB_A_TST_MASK, buf[0] + bias[3]); ++ MTK_PHY_DA_TX_I2MPB_A_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TST_MASK, ++ buf[0] + bias[3])); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, +- MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, (buf[1] + bias[4]) << 8); ++ MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, ++ buf[1] + bias[4])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, +- MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, buf[1] + bias[5]); ++ MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, ++ buf[1] + bias[5])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, +- MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, (buf[1] + bias[6]) << 8); ++ MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, ++ buf[1] + bias[6])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, +- MTK_PHY_DA_TX_I2MPB_B_TST_MASK, buf[1] + bias[7]); ++ MTK_PHY_DA_TX_I2MPB_B_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TST_MASK, ++ buf[1] + bias[7])); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, +- MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, (buf[2] + bias[8]) << 8); ++ MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, ++ buf[2] + bias[8])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, +- MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, buf[2] + bias[9]); ++ MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, ++ buf[2] + bias[9])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, +- MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, (buf[2] + bias[10]) << 8); ++ MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, ++ buf[2] + bias[10])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, +- MTK_PHY_DA_TX_I2MPB_C_TST_MASK, buf[2] + bias[11]); ++ MTK_PHY_DA_TX_I2MPB_C_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TST_MASK, ++ buf[2] + bias[11])); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, +- MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, (buf[3] + bias[12]) << 8); ++ MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, ++ buf[3] + bias[12])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, +- MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, buf[3] + bias[13]); ++ MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, ++ buf[3] + bias[13])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, +- MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, (buf[3] + bias[14]) << 8); ++ MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, ++ buf[3] + bias[14])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, +- MTK_PHY_DA_TX_I2MPB_D_TST_MASK, buf[3] + bias[15]); ++ MTK_PHY_DA_TX_I2MPB_D_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TST_MASK, ++ buf[3] + bias[15])); + + return 0; + } +@@ -663,7 +696,8 @@ static int tx_vcm_cal_sw(struct phy_devi + goto restore; + + /* We calibrate TX-VCM in different logic. Check upper index and then +- * lower index. If this calibration is valid, apply lower index's result. ++ * lower index. If this calibration is valid, apply lower index's ++ * result. + */ + ret = upper_ret - lower_ret; + if (ret == 1) { +@@ -692,7 +726,8 @@ static int tx_vcm_cal_sw(struct phy_devi + } else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 && + lower_ret == 0) { + ret = 0; +- phydev_warn(phydev, "TX-VCM SW cal result at high margin 0x%x\n", ++ phydev_warn(phydev, ++ "TX-VCM SW cal result at high margin 0x%x\n", + upper_idx); + } else { + ret = -EINVAL; +@@ -796,7 +831,8 @@ static void mt7981_phy_finetune(struct p + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, +- MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, ++ MTK_PHY_TR_OPEN_LOOP_EN_MASK | ++ MTK_PHY_LPF_X_AVERAGE_MASK, + BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); + + /* rg_tr_lpf_cnt_val = 512 */ +@@ -865,7 +901,8 @@ static void mt7988_phy_finetune(struct p + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, +- MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, ++ MTK_PHY_TR_OPEN_LOOP_EN_MASK | ++ MTK_PHY_LPF_X_AVERAGE_MASK, + BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0xa)); + + /* rg_tr_lpf_cnt_val = 1023 */ +@@ -977,7 +1014,8 @@ static void mt798x_phy_eee(struct phy_de + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3); +- __phy_modify(phydev, MTK_PHY_LPI_REG_14, MTK_PHY_LPI_WAKE_TIMER_1000_MASK, ++ __phy_modify(phydev, MTK_PHY_LPI_REG_14, ++ MTK_PHY_LPI_WAKE_TIMER_1000_MASK, + FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c)); + + __phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK, +@@ -987,7 +1025,8 @@ static void mt798x_phy_eee(struct phy_de + phy_modify_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, + MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, +- FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, 0xff)); ++ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, ++ 0xff)); + } + + static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item, +@@ -1147,7 +1186,8 @@ static int mt798x_phy_hw_led_on_set(stru + (index ? 16 : 0), &priv->led_state); + if (changed) + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED1_ON_CTRL : ++ MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED_ON_MASK, + on ? MTK_PHY_LED_ON_FORCE_ON : 0); + else +@@ -1157,7 +1197,8 @@ static int mt798x_phy_hw_led_on_set(stru + static int mt798x_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, + bool blinking) + { +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + (index ? 16 : 0); ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); + struct mtk_socphy_priv *priv = phydev->priv; + bool changed; + +@@ -1170,8 +1211,10 @@ static int mt798x_phy_hw_led_blink_set(s + (index ? 16 : 0), &priv->led_state); + if (changed) + return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_BLINK_CTRL : MTK_PHY_LED0_BLINK_CTRL, +- blinking ? MTK_PHY_LED_BLINK_FORCE_BLINK : 0); ++ MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL, ++ blinking ? ++ MTK_PHY_LED_BLINK_FORCE_BLINK : 0); + else + return 0; + } +@@ -1237,7 +1280,8 @@ static int mt798x_phy_led_hw_is_supporte + static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) + { +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + (index ? 16 : 0); ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); + unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); + unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); + struct mtk_socphy_priv *priv = phydev->priv; +@@ -1258,8 +1302,8 @@ static int mt798x_phy_led_hw_control_get + if (blink < 0) + return -EIO; + +- if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX | +- MTK_PHY_LED_ON_LINKDOWN)) || ++ if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | ++ MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) || + (blink & (MTK_PHY_LED_BLINK_RX | MTK_PHY_LED_BLINK_TX))) + set_bit(bit_netdev, &priv->led_state); + else +@@ -1333,17 +1377,23 @@ static int mt798x_phy_led_hw_control_set + + if (rules & BIT(TRIGGER_NETDEV_RX)) { + blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? MTK_PHY_LED_BLINK_10RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? MTK_PHY_LED_BLINK_100RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? MTK_PHY_LED_BLINK_1000RX : 0)) : ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000RX : 0)) : + MTK_PHY_LED_BLINK_RX; + } + + if (rules & BIT(TRIGGER_NETDEV_TX)) { + blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? MTK_PHY_LED_BLINK_10TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? MTK_PHY_LED_BLINK_100TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? MTK_PHY_LED_BLINK_1000TX : 0)) : ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000TX : 0)) : + MTK_PHY_LED_BLINK_TX; + } + +@@ -1400,7 +1450,8 @@ static int mt7988_phy_fix_leds_polaritie + /* Only now setup pinctrl to avoid bogus blinking */ + pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led"); + if (IS_ERR(pinctrl)) +- dev_err(&phydev->mdio.bus->dev, "Failed to setup PHY LED pinctrl\n"); ++ dev_err(&phydev->mdio.bus->dev, ++ "Failed to setup PHY LED pinctrl\n"); + + return 0; + } diff --git a/target/linux/generic/backport-6.12/720-03-v6.13-net-phy-mediatek-ge-soc-Propagate-error-code-correct.patch b/target/linux/generic/backport-6.12/720-03-v6.13-net-phy-mediatek-ge-soc-Propagate-error-code-correct.patch new file mode 100644 index 00000000000..1f6f5f5df61 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-03-v6.13-net-phy-mediatek-ge-soc-Propagate-error-code-correct.patch @@ -0,0 +1,40 @@ +From bcbbfb4f62c4ba35783cc617997a2e92d91e3940 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 17 Oct 2024 11:22:13 +0800 +Subject: [PATCH 03/20] net: phy: mediatek-ge-soc: Propagate error code + correctly in cal_cycle() + +This patch propagates error code correctly in cal_cycle() +and improve with FIELD_GET(). + +Signed-off-by: SkyLake.Huang +Reviewed-by: Simon Horman +Signed-off-by: Andrew Lunn +--- + drivers/net/phy/mediatek-ge-soc.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/mediatek-ge-soc.c ++++ b/drivers/net/phy/mediatek-ge-soc.c +@@ -110,7 +110,7 @@ + #define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0) + + #define MTK_PHY_RG_AD_CAL_COMP 0x17a +-#define MTK_PHY_AD_CAL_COMP_OUT_SHIFT (8) ++#define MTK_PHY_AD_CAL_COMP_OUT_MASK GENMASK(8, 8) + + #define MTK_PHY_RG_AD_CAL_CLK 0x17b + #define MTK_PHY_DA_CAL_CLK BIT(0) +@@ -351,8 +351,10 @@ static int cal_cycle(struct phy_device * + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, + MTK_PHY_DA_CALIN_FLAG); +- ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP) >> +- MTK_PHY_AD_CAL_COMP_OUT_SHIFT; ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP); ++ if (ret < 0) ++ return ret; ++ ret = FIELD_GET(MTK_PHY_AD_CAL_COMP_OUT_MASK, ret); + phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret); + + return ret; diff --git a/target/linux/generic/backport-6.12/720-04-v6.13-net-phy-mediatek-Re-organize-MediaTek-ethernet-phy-d.patch b/target/linux/generic/backport-6.12/720-04-v6.13-net-phy-mediatek-Re-organize-MediaTek-ethernet-phy-d.patch new file mode 100644 index 00000000000..81e25109121 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-04-v6.13-net-phy-mediatek-Re-organize-MediaTek-ethernet-phy-d.patch @@ -0,0 +1,3565 @@ +From e062f073dc0df4fcd338043cb0b69b6bcd31e4af Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Sat, 9 Nov 2024 00:34:51 +0800 +Subject: [PATCH 04/20] net: phy: mediatek: Re-organize MediaTek ethernet phy + drivers + +Re-organize MediaTek ethernet phy driver files and get ready to integrate +some common functions and add new 2.5G phy driver. +mtk-ge.c: MT7530 Gphy on MT7621 & MT7531 Gphy +mtk-ge-soc.c: Built-in Gphy on MT7981 & Built-in switch Gphy on MT7988 +mtk-2p5ge.c: Planned for built-in 2.5G phy on MT7988 + +Reviewed-by: Andrew Lunn +Signed-off-by: SkyLake.Huang +Signed-off-by: David S. Miller +--- + MAINTAINERS | 4 ++-- + drivers/net/phy/Kconfig | 17 +------------- + drivers/net/phy/Makefile | 3 +-- + drivers/net/phy/mediatek/Kconfig | 22 +++++++++++++++++++ + drivers/net/phy/mediatek/Makefile | 3 +++ + .../mtk-ge-soc.c} | 0 + .../phy/{mediatek-ge.c => mediatek/mtk-ge.c} | 0 + 7 files changed, 29 insertions(+), 20 deletions(-) + create mode 100644 drivers/net/phy/mediatek/Kconfig + create mode 100644 drivers/net/phy/mediatek/Makefile + rename drivers/net/phy/{mediatek-ge-soc.c => mediatek/mtk-ge-soc.c} (100%) + rename drivers/net/phy/{mediatek-ge.c => mediatek/mtk-ge.c} (100%) + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -14427,8 +14427,8 @@ M: Qingfang Deng + M: SkyLake Huang + L: netdev@vger.kernel.org + S: Maintained +-F: drivers/net/phy/mediatek-ge-soc.c +-F: drivers/net/phy/mediatek-ge.c ++F: drivers/net/phy/mediatek/mtk-ge-soc.c ++F: drivers/net/phy/mediatek/mtk-ge.c + F: drivers/phy/mediatek/phy-mtk-xfi-tphy.c + + MEDIATEK I2C CONTROLLER DRIVER +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -266,22 +266,7 @@ config MAXLINEAR_GPHY + Support for the Maxlinear GPY115, GPY211, GPY212, GPY215, + GPY241, GPY245 PHYs. + +-config MEDIATEK_GE_PHY +- tristate "MediaTek Gigabit Ethernet PHYs" +- help +- Supports the MediaTek Gigabit Ethernet PHYs. +- +-config MEDIATEK_GE_SOC_PHY +- tristate "MediaTek SoC Ethernet PHYs" +- depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST +- depends on NVMEM_MTK_EFUSE +- help +- Supports MediaTek SoC built-in Gigabit Ethernet PHYs. +- +- Include support for built-in Ethernet PHYs which are present in +- the MT7981 and MT7988 SoCs. These PHYs need calibration data +- present in the SoCs efuse and will dynamically calibrate VCM +- (common-mode voltage) during startup. ++source "drivers/net/phy/mediatek/Kconfig" + + config MICREL_PHY + tristate "Micrel PHYs" +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -74,8 +74,7 @@ obj-$(CONFIG_MARVELL_PHY) += marvell.o + obj-$(CONFIG_MARVELL_88Q2XXX_PHY) += marvell-88q2xxx.o + obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o + obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o +-obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o +-obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mediatek-ge-soc.o ++obj-y += mediatek/ + obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o + obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o + obj-$(CONFIG_MICREL_PHY) += micrel.o +--- /dev/null ++++ b/drivers/net/phy/mediatek/Kconfig +@@ -0,0 +1,22 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++config MEDIATEK_GE_PHY ++ tristate "MediaTek Gigabit Ethernet PHYs" ++ help ++ Supports the MediaTek non-built-in Gigabit Ethernet PHYs. ++ ++ Non-built-in Gigabit Ethernet PHYs include mt7530/mt7531. ++ You may find mt7530 inside mt7621. This driver shares some ++ common operations with MediaTek SoC built-in Gigabit ++ Ethernet PHYs. ++ ++config MEDIATEK_GE_SOC_PHY ++ tristate "MediaTek SoC Ethernet PHYs" ++ depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST ++ depends on NVMEM_MTK_EFUSE ++ help ++ Supports MediaTek SoC built-in Gigabit Ethernet PHYs. ++ ++ Include support for built-in Ethernet PHYs which are present in ++ the MT7981 and MT7988 SoCs. These PHYs need calibration data ++ present in the SoCs efuse and will dynamically calibrate VCM ++ (common-mode voltage) during startup. +--- /dev/null ++++ b/drivers/net/phy/mediatek/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_MEDIATEK_GE_PHY) += mtk-ge.o ++obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mtk-ge-soc.o +--- a/drivers/net/phy/mediatek-ge-soc.c ++++ /dev/null +@@ -1,1610 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define MTK_GPHY_ID_MT7981 0x03a29461 +-#define MTK_GPHY_ID_MT7988 0x03a29481 +- +-#define MTK_EXT_PAGE_ACCESS 0x1f +-#define MTK_PHY_PAGE_STANDARD 0x0000 +-#define MTK_PHY_PAGE_EXTENDED_3 0x0003 +- +-#define MTK_PHY_LPI_REG_14 0x14 +-#define MTK_PHY_LPI_WAKE_TIMER_1000_MASK GENMASK(8, 0) +- +-#define MTK_PHY_LPI_REG_1c 0x1c +-#define MTK_PHY_SMI_DET_ON_THRESH_MASK GENMASK(13, 8) +- +-#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +-#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 +- +-#define ANALOG_INTERNAL_OPERATION_MAX_US 20 +-#define TXRESERVE_MIN 0 +-#define TXRESERVE_MAX 7 +- +-#define MTK_PHY_ANARG_RG 0x10 +-#define MTK_PHY_TCLKOFFSET_MASK GENMASK(12, 8) +- +-/* Registers on MDIO_MMD_VEND1 */ +-#define MTK_PHY_TXVLD_DA_RG 0x12 +-#define MTK_PHY_DA_TX_I2MPB_A_GBE_MASK GENMASK(15, 10) +-#define MTK_PHY_DA_TX_I2MPB_A_TBT_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_A2 0x16 +-#define MTK_PHY_DA_TX_I2MPB_A_HBT_MASK GENMASK(15, 10) +-#define MTK_PHY_DA_TX_I2MPB_A_TST_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_B1 0x17 +-#define MTK_PHY_DA_TX_I2MPB_B_GBE_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_B_TBT_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_B2 0x18 +-#define MTK_PHY_DA_TX_I2MPB_B_HBT_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_B_TST_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_C1 0x19 +-#define MTK_PHY_DA_TX_I2MPB_C_GBE_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_C_TBT_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_C2 0x20 +-#define MTK_PHY_DA_TX_I2MPB_C_HBT_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_C_TST_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_D1 0x21 +-#define MTK_PHY_DA_TX_I2MPB_D_GBE_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_D_TBT_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_D2 0x22 +-#define MTK_PHY_DA_TX_I2MPB_D_HBT_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_D_TST_MASK GENMASK(5, 0) +- +-#define MTK_PHY_RXADC_CTRL_RG7 0xc6 +-#define MTK_PHY_DA_AD_BUF_BIAS_LP_MASK GENMASK(9, 8) +- +-#define MTK_PHY_RXADC_CTRL_RG9 0xc8 +-#define MTK_PHY_DA_RX_PSBN_TBT_MASK GENMASK(14, 12) +-#define MTK_PHY_DA_RX_PSBN_HBT_MASK GENMASK(10, 8) +-#define MTK_PHY_DA_RX_PSBN_GBE_MASK GENMASK(6, 4) +-#define MTK_PHY_DA_RX_PSBN_LP_MASK GENMASK(2, 0) +- +-#define MTK_PHY_LDO_OUTPUT_V 0xd7 +- +-#define MTK_PHY_RG_ANA_CAL_RG0 0xdb +-#define MTK_PHY_RG_CAL_CKINV BIT(12) +-#define MTK_PHY_RG_ANA_CALEN BIT(8) +-#define MTK_PHY_RG_ZCALEN_A BIT(0) +- +-#define MTK_PHY_RG_ANA_CAL_RG1 0xdc +-#define MTK_PHY_RG_ZCALEN_B BIT(12) +-#define MTK_PHY_RG_ZCALEN_C BIT(8) +-#define MTK_PHY_RG_ZCALEN_D BIT(4) +-#define MTK_PHY_RG_TXVOS_CALEN BIT(0) +- +-#define MTK_PHY_RG_ANA_CAL_RG5 0xe0 +-#define MTK_PHY_RG_REXT_TRIM_MASK GENMASK(13, 8) +- +-#define MTK_PHY_RG_TX_FILTER 0xfe +- +-#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120 0x120 +-#define MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK GENMASK(12, 8) +-#define MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK GENMASK(4, 0) +- +-#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122 0x122 +-#define MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK GENMASK(7, 0) +- +-#define MTK_PHY_RG_TESTMUX_ADC_CTRL 0x144 +-#define MTK_PHY_RG_TXEN_DIG_MASK GENMASK(5, 5) +- +-#define MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B 0x172 +-#define MTK_PHY_CR_TX_AMP_OFFSET_A_MASK GENMASK(13, 8) +-#define MTK_PHY_CR_TX_AMP_OFFSET_B_MASK GENMASK(6, 0) +- +-#define MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D 0x173 +-#define MTK_PHY_CR_TX_AMP_OFFSET_C_MASK GENMASK(13, 8) +-#define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0) +- +-#define MTK_PHY_RG_AD_CAL_COMP 0x17a +-#define MTK_PHY_AD_CAL_COMP_OUT_MASK GENMASK(8, 8) +- +-#define MTK_PHY_RG_AD_CAL_CLK 0x17b +-#define MTK_PHY_DA_CAL_CLK BIT(0) +- +-#define MTK_PHY_RG_AD_CALIN 0x17c +-#define MTK_PHY_DA_CALIN_FLAG BIT(0) +- +-#define MTK_PHY_RG_DASN_DAC_IN0_A 0x17d +-#define MTK_PHY_DASN_DAC_IN0_A_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN0_B 0x17e +-#define MTK_PHY_DASN_DAC_IN0_B_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN0_C 0x17f +-#define MTK_PHY_DASN_DAC_IN0_C_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN0_D 0x180 +-#define MTK_PHY_DASN_DAC_IN0_D_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN1_A 0x181 +-#define MTK_PHY_DASN_DAC_IN1_A_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN1_B 0x182 +-#define MTK_PHY_DASN_DAC_IN1_B_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN1_C 0x183 +-#define MTK_PHY_DASN_DAC_IN1_C_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN1_D 0x184 +-#define MTK_PHY_DASN_DAC_IN1_D_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DEV1E_REG19b 0x19b +-#define MTK_PHY_BYPASS_DSP_LPI_READY BIT(8) +- +-#define MTK_PHY_RG_LP_IIR2_K1_L 0x22a +-#define MTK_PHY_RG_LP_IIR2_K1_U 0x22b +-#define MTK_PHY_RG_LP_IIR2_K2_L 0x22c +-#define MTK_PHY_RG_LP_IIR2_K2_U 0x22d +-#define MTK_PHY_RG_LP_IIR2_K3_L 0x22e +-#define MTK_PHY_RG_LP_IIR2_K3_U 0x22f +-#define MTK_PHY_RG_LP_IIR2_K4_L 0x230 +-#define MTK_PHY_RG_LP_IIR2_K4_U 0x231 +-#define MTK_PHY_RG_LP_IIR2_K5_L 0x232 +-#define MTK_PHY_RG_LP_IIR2_K5_U 0x233 +- +-#define MTK_PHY_RG_DEV1E_REG234 0x234 +-#define MTK_PHY_TR_OPEN_LOOP_EN_MASK GENMASK(0, 0) +-#define MTK_PHY_LPF_X_AVERAGE_MASK GENMASK(7, 4) +-#define MTK_PHY_TR_LP_IIR_EEE_EN BIT(12) +- +-#define MTK_PHY_RG_LPF_CNT_VAL 0x235 +- +-#define MTK_PHY_RG_DEV1E_REG238 0x238 +-#define MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK GENMASK(8, 0) +-#define MTK_PHY_LPI_SLV_SEND_TX_EN BIT(12) +- +-#define MTK_PHY_RG_DEV1E_REG239 0x239 +-#define MTK_PHY_LPI_SEND_LOC_TIMER_MASK GENMASK(8, 0) +-#define MTK_PHY_LPI_TXPCS_LOC_RCV BIT(12) +- +-#define MTK_PHY_RG_DEV1E_REG27C 0x27c +-#define MTK_PHY_VGASTATE_FFE_THR_ST1_MASK GENMASK(12, 8) +-#define MTK_PHY_RG_DEV1E_REG27D 0x27d +-#define MTK_PHY_VGASTATE_FFE_THR_ST2_MASK GENMASK(4, 0) +- +-#define MTK_PHY_RG_DEV1E_REG2C7 0x2c7 +-#define MTK_PHY_MAX_GAIN_MASK GENMASK(4, 0) +-#define MTK_PHY_MIN_GAIN_MASK GENMASK(12, 8) +- +-#define MTK_PHY_RG_DEV1E_REG2D1 0x2d1 +-#define MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK GENMASK(7, 0) +-#define MTK_PHY_LPI_SKIP_SD_SLV_TR BIT(8) +-#define MTK_PHY_LPI_TR_READY BIT(9) +-#define MTK_PHY_LPI_VCO_EEE_STG0_EN BIT(10) +- +-#define MTK_PHY_RG_DEV1E_REG323 0x323 +-#define MTK_PHY_EEE_WAKE_MAS_INT_DC BIT(0) +-#define MTK_PHY_EEE_WAKE_SLV_INT_DC BIT(4) +- +-#define MTK_PHY_RG_DEV1E_REG324 0x324 +-#define MTK_PHY_SMI_DETCNT_MAX_MASK GENMASK(5, 0) +-#define MTK_PHY_SMI_DET_MAX_EN BIT(8) +- +-#define MTK_PHY_RG_DEV1E_REG326 0x326 +-#define MTK_PHY_LPI_MODE_SD_ON BIT(0) +-#define MTK_PHY_RESET_RANDUPD_CNT BIT(1) +-#define MTK_PHY_TREC_UPDATE_ENAB_CLR BIT(2) +-#define MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF BIT(4) +-#define MTK_PHY_TR_READY_SKIP_AFE_WAKEUP BIT(5) +- +-#define MTK_PHY_LDO_PUMP_EN_PAIRAB 0x502 +-#define MTK_PHY_LDO_PUMP_EN_PAIRCD 0x503 +- +-#define MTK_PHY_DA_TX_R50_PAIR_A 0x53d +-#define MTK_PHY_DA_TX_R50_PAIR_B 0x53e +-#define MTK_PHY_DA_TX_R50_PAIR_C 0x53f +-#define MTK_PHY_DA_TX_R50_PAIR_D 0x540 +- +-/* Registers on MDIO_MMD_VEND2 */ +-#define MTK_PHY_LED0_ON_CTRL 0x24 +-#define MTK_PHY_LED1_ON_CTRL 0x26 +-#define MTK_PHY_LED_ON_MASK GENMASK(6, 0) +-#define MTK_PHY_LED_ON_LINK1000 BIT(0) +-#define MTK_PHY_LED_ON_LINK100 BIT(1) +-#define MTK_PHY_LED_ON_LINK10 BIT(2) +-#define MTK_PHY_LED_ON_LINK (MTK_PHY_LED_ON_LINK10 |\ +- MTK_PHY_LED_ON_LINK100 |\ +- MTK_PHY_LED_ON_LINK1000) +-#define MTK_PHY_LED_ON_LINKDOWN BIT(3) +-#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ +-#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ +-#define MTK_PHY_LED_ON_FORCE_ON BIT(6) +-#define MTK_PHY_LED_ON_POLARITY BIT(14) +-#define MTK_PHY_LED_ON_ENABLE BIT(15) +- +-#define MTK_PHY_LED0_BLINK_CTRL 0x25 +-#define MTK_PHY_LED1_BLINK_CTRL 0x27 +-#define MTK_PHY_LED_BLINK_1000TX BIT(0) +-#define MTK_PHY_LED_BLINK_1000RX BIT(1) +-#define MTK_PHY_LED_BLINK_100TX BIT(2) +-#define MTK_PHY_LED_BLINK_100RX BIT(3) +-#define MTK_PHY_LED_BLINK_10TX BIT(4) +-#define MTK_PHY_LED_BLINK_10RX BIT(5) +-#define MTK_PHY_LED_BLINK_RX (MTK_PHY_LED_BLINK_10RX |\ +- MTK_PHY_LED_BLINK_100RX |\ +- MTK_PHY_LED_BLINK_1000RX) +-#define MTK_PHY_LED_BLINK_TX (MTK_PHY_LED_BLINK_10TX |\ +- MTK_PHY_LED_BLINK_100TX |\ +- MTK_PHY_LED_BLINK_1000TX) +-#define MTK_PHY_LED_BLINK_COLLISION BIT(6) +-#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) +-#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) +-#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9) +- +-#define MTK_PHY_LED1_DEFAULT_POLARITIES BIT(1) +- +-#define MTK_PHY_RG_BG_RASEL 0x115 +-#define MTK_PHY_RG_BG_RASEL_MASK GENMASK(2, 0) +- +-/* 'boottrap' register reflecting the configuration of the 4 PHY LEDs */ +-#define RG_GPIO_MISC_TPBANK0 0x6f0 +-#define RG_GPIO_MISC_TPBANK0_BOOTMODE GENMASK(11, 8) +- +-/* These macro privides efuse parsing for internal phy. */ +-#define EFS_DA_TX_I2MPB_A(x) (((x) >> 0) & GENMASK(5, 0)) +-#define EFS_DA_TX_I2MPB_B(x) (((x) >> 6) & GENMASK(5, 0)) +-#define EFS_DA_TX_I2MPB_C(x) (((x) >> 12) & GENMASK(5, 0)) +-#define EFS_DA_TX_I2MPB_D(x) (((x) >> 18) & GENMASK(5, 0)) +-#define EFS_DA_TX_AMP_OFFSET_A(x) (((x) >> 24) & GENMASK(5, 0)) +- +-#define EFS_DA_TX_AMP_OFFSET_B(x) (((x) >> 0) & GENMASK(5, 0)) +-#define EFS_DA_TX_AMP_OFFSET_C(x) (((x) >> 6) & GENMASK(5, 0)) +-#define EFS_DA_TX_AMP_OFFSET_D(x) (((x) >> 12) & GENMASK(5, 0)) +-#define EFS_DA_TX_R50_A(x) (((x) >> 18) & GENMASK(5, 0)) +-#define EFS_DA_TX_R50_B(x) (((x) >> 24) & GENMASK(5, 0)) +- +-#define EFS_DA_TX_R50_C(x) (((x) >> 0) & GENMASK(5, 0)) +-#define EFS_DA_TX_R50_D(x) (((x) >> 6) & GENMASK(5, 0)) +- +-#define EFS_RG_BG_RASEL(x) (((x) >> 4) & GENMASK(2, 0)) +-#define EFS_RG_REXT_TRIM(x) (((x) >> 7) & GENMASK(5, 0)) +- +-enum { +- NO_PAIR, +- PAIR_A, +- PAIR_B, +- PAIR_C, +- PAIR_D, +-}; +- +-enum calibration_mode { +- EFUSE_K, +- SW_K +-}; +- +-enum CAL_ITEM { +- REXT, +- TX_OFFSET, +- TX_AMP, +- TX_R50, +- TX_VCM +-}; +- +-enum CAL_MODE { +- EFUSE_M, +- SW_M +-}; +- +-#define MTK_PHY_LED_STATE_FORCE_ON 0 +-#define MTK_PHY_LED_STATE_FORCE_BLINK 1 +-#define MTK_PHY_LED_STATE_NETDEV 2 +- +-struct mtk_socphy_priv { +- unsigned long led_state; +-}; +- +-struct mtk_socphy_shared { +- u32 boottrap; +- struct mtk_socphy_priv priv[4]; +-}; +- +-static int mtk_socphy_read_page(struct phy_device *phydev) +-{ +- return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +-} +- +-static int mtk_socphy_write_page(struct phy_device *phydev, int page) +-{ +- return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +-} +- +-/* One calibration cycle consists of: +- * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high +- * until AD_CAL_COMP is ready to output calibration result. +- * 2.Wait until DA_CAL_CLK is available. +- * 3.Fetch AD_CAL_COMP_OUT. +- */ +-static int cal_cycle(struct phy_device *phydev, int devad, +- u32 regnum, u16 mask, u16 cal_val) +-{ +- int reg_val; +- int ret; +- +- phy_modify_mmd(phydev, devad, regnum, +- mask, cal_val); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, +- MTK_PHY_DA_CALIN_FLAG); +- +- ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_AD_CAL_CLK, reg_val, +- reg_val & MTK_PHY_DA_CAL_CLK, 500, +- ANALOG_INTERNAL_OPERATION_MAX_US, +- false); +- if (ret) { +- phydev_err(phydev, "Calibration cycle timeout\n"); +- return ret; +- } +- +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, +- MTK_PHY_DA_CALIN_FLAG); +- ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP); +- if (ret < 0) +- return ret; +- ret = FIELD_GET(MTK_PHY_AD_CAL_COMP_OUT_MASK, ret); +- phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret); +- +- return ret; +-} +- +-static int rext_fill_result(struct phy_device *phydev, u16 *buf) +-{ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, +- MTK_PHY_RG_REXT_TRIM_MASK, buf[0] << 8); +- phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_BG_RASEL, +- MTK_PHY_RG_BG_RASEL_MASK, buf[1]); +- +- return 0; +-} +- +-static int rext_cal_efuse(struct phy_device *phydev, u32 *buf) +-{ +- u16 rext_cal_val[2]; +- +- rext_cal_val[0] = EFS_RG_REXT_TRIM(buf[3]); +- rext_cal_val[1] = EFS_RG_BG_RASEL(buf[3]); +- rext_fill_result(phydev, rext_cal_val); +- +- return 0; +-} +- +-static int tx_offset_fill_result(struct phy_device *phydev, u16 *buf) +-{ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, +- MTK_PHY_CR_TX_AMP_OFFSET_A_MASK, buf[0] << 8); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, +- MTK_PHY_CR_TX_AMP_OFFSET_B_MASK, buf[1]); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, +- MTK_PHY_CR_TX_AMP_OFFSET_C_MASK, buf[2] << 8); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, +- MTK_PHY_CR_TX_AMP_OFFSET_D_MASK, buf[3]); +- +- return 0; +-} +- +-static int tx_offset_cal_efuse(struct phy_device *phydev, u32 *buf) +-{ +- u16 tx_offset_cal_val[4]; +- +- tx_offset_cal_val[0] = EFS_DA_TX_AMP_OFFSET_A(buf[0]); +- tx_offset_cal_val[1] = EFS_DA_TX_AMP_OFFSET_B(buf[1]); +- tx_offset_cal_val[2] = EFS_DA_TX_AMP_OFFSET_C(buf[1]); +- tx_offset_cal_val[3] = EFS_DA_TX_AMP_OFFSET_D(buf[1]); +- +- tx_offset_fill_result(phydev, tx_offset_cal_val); +- +- return 0; +-} +- +-static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf) +-{ +- const int vals_9481[16] = { 10, 6, 6, 10, +- 10, 6, 6, 10, +- 10, 6, 6, 10, +- 10, 6, 6, 10 }; +- const int vals_9461[16] = { 7, 1, 4, 7, +- 7, 1, 4, 7, +- 7, 1, 4, 7, +- 7, 1, 4, 7 }; +- int bias[16] = {}; +- int i; +- +- switch (phydev->drv->phy_id) { +- case MTK_GPHY_ID_MT7981: +- /* We add some calibration to efuse values +- * due to board level influence. +- * GBE: +7, TBT: +1, HBT: +4, TST: +7 +- */ +- memcpy(bias, (const void *)vals_9461, sizeof(bias)); +- break; +- case MTK_GPHY_ID_MT7988: +- memcpy(bias, (const void *)vals_9481, sizeof(bias)); +- break; +- } +- +- /* Prevent overflow */ +- for (i = 0; i < 12; i++) { +- if (buf[i >> 2] + bias[i] > 63) { +- buf[i >> 2] = 63; +- bias[i] = 0; +- } +- } +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, +- MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, +- buf[0] + bias[0])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, +- MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, +- buf[0] + bias[1])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, +- MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, +- buf[0] + bias[2])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, +- MTK_PHY_DA_TX_I2MPB_A_TST_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TST_MASK, +- buf[0] + bias[3])); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, +- MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, +- buf[1] + bias[4])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, +- MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, +- buf[1] + bias[5])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, +- MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, +- buf[1] + bias[6])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, +- MTK_PHY_DA_TX_I2MPB_B_TST_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TST_MASK, +- buf[1] + bias[7])); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, +- MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, +- buf[2] + bias[8])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, +- MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, +- buf[2] + bias[9])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, +- MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, +- buf[2] + bias[10])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, +- MTK_PHY_DA_TX_I2MPB_C_TST_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TST_MASK, +- buf[2] + bias[11])); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, +- MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, +- buf[3] + bias[12])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, +- MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, +- buf[3] + bias[13])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, +- MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, +- buf[3] + bias[14])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, +- MTK_PHY_DA_TX_I2MPB_D_TST_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TST_MASK, +- buf[3] + bias[15])); +- +- return 0; +-} +- +-static int tx_amp_cal_efuse(struct phy_device *phydev, u32 *buf) +-{ +- u16 tx_amp_cal_val[4]; +- +- tx_amp_cal_val[0] = EFS_DA_TX_I2MPB_A(buf[0]); +- tx_amp_cal_val[1] = EFS_DA_TX_I2MPB_B(buf[0]); +- tx_amp_cal_val[2] = EFS_DA_TX_I2MPB_C(buf[0]); +- tx_amp_cal_val[3] = EFS_DA_TX_I2MPB_D(buf[0]); +- tx_amp_fill_result(phydev, tx_amp_cal_val); +- +- return 0; +-} +- +-static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val, +- u8 txg_calen_x) +-{ +- int bias = 0; +- u16 reg, val; +- +- if (phydev->drv->phy_id == MTK_GPHY_ID_MT7988) +- bias = -1; +- +- val = clamp_val(bias + tx_r50_cal_val, 0, 63); +- +- switch (txg_calen_x) { +- case PAIR_A: +- reg = MTK_PHY_DA_TX_R50_PAIR_A; +- break; +- case PAIR_B: +- reg = MTK_PHY_DA_TX_R50_PAIR_B; +- break; +- case PAIR_C: +- reg = MTK_PHY_DA_TX_R50_PAIR_C; +- break; +- case PAIR_D: +- reg = MTK_PHY_DA_TX_R50_PAIR_D; +- break; +- default: +- return -EINVAL; +- } +- +- phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, val | val << 8); +- +- return 0; +-} +- +-static int tx_r50_cal_efuse(struct phy_device *phydev, u32 *buf, +- u8 txg_calen_x) +-{ +- u16 tx_r50_cal_val; +- +- switch (txg_calen_x) { +- case PAIR_A: +- tx_r50_cal_val = EFS_DA_TX_R50_A(buf[1]); +- break; +- case PAIR_B: +- tx_r50_cal_val = EFS_DA_TX_R50_B(buf[1]); +- break; +- case PAIR_C: +- tx_r50_cal_val = EFS_DA_TX_R50_C(buf[2]); +- break; +- case PAIR_D: +- tx_r50_cal_val = EFS_DA_TX_R50_D(buf[2]); +- break; +- default: +- return -EINVAL; +- } +- tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); +- +- return 0; +-} +- +-static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x) +-{ +- u8 lower_idx, upper_idx, txreserve_val; +- u8 lower_ret, upper_ret; +- int ret; +- +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, +- MTK_PHY_RG_ANA_CALEN); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, +- MTK_PHY_RG_CAL_CKINV); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_TXVOS_CALEN); +- +- switch (rg_txreserve_x) { +- case PAIR_A: +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN0_A, +- MTK_PHY_DASN_DAC_IN0_A_MASK); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN1_A, +- MTK_PHY_DASN_DAC_IN1_A_MASK); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_ANA_CAL_RG0, +- MTK_PHY_RG_ZCALEN_A); +- break; +- case PAIR_B: +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN0_B, +- MTK_PHY_DASN_DAC_IN0_B_MASK); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN1_B, +- MTK_PHY_DASN_DAC_IN1_B_MASK); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_ZCALEN_B); +- break; +- case PAIR_C: +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN0_C, +- MTK_PHY_DASN_DAC_IN0_C_MASK); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN1_C, +- MTK_PHY_DASN_DAC_IN1_C_MASK); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_ZCALEN_C); +- break; +- case PAIR_D: +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN0_D, +- MTK_PHY_DASN_DAC_IN0_D_MASK); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN1_D, +- MTK_PHY_DASN_DAC_IN1_D_MASK); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_ZCALEN_D); +- break; +- default: +- ret = -EINVAL; +- goto restore; +- } +- +- lower_idx = TXRESERVE_MIN; +- upper_idx = TXRESERVE_MAX; +- +- phydev_dbg(phydev, "Start TX-VCM SW cal.\n"); +- while ((upper_idx - lower_idx) > 1) { +- txreserve_val = DIV_ROUND_CLOSEST(lower_idx + upper_idx, 2); +- ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, +- MTK_PHY_DA_RX_PSBN_TBT_MASK | +- MTK_PHY_DA_RX_PSBN_HBT_MASK | +- MTK_PHY_DA_RX_PSBN_GBE_MASK | +- MTK_PHY_DA_RX_PSBN_LP_MASK, +- txreserve_val << 12 | txreserve_val << 8 | +- txreserve_val << 4 | txreserve_val); +- if (ret == 1) { +- upper_idx = txreserve_val; +- upper_ret = ret; +- } else if (ret == 0) { +- lower_idx = txreserve_val; +- lower_ret = ret; +- } else { +- goto restore; +- } +- } +- +- if (lower_idx == TXRESERVE_MIN) { +- lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RXADC_CTRL_RG9, +- MTK_PHY_DA_RX_PSBN_TBT_MASK | +- MTK_PHY_DA_RX_PSBN_HBT_MASK | +- MTK_PHY_DA_RX_PSBN_GBE_MASK | +- MTK_PHY_DA_RX_PSBN_LP_MASK, +- lower_idx << 12 | lower_idx << 8 | +- lower_idx << 4 | lower_idx); +- ret = lower_ret; +- } else if (upper_idx == TXRESERVE_MAX) { +- upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RXADC_CTRL_RG9, +- MTK_PHY_DA_RX_PSBN_TBT_MASK | +- MTK_PHY_DA_RX_PSBN_HBT_MASK | +- MTK_PHY_DA_RX_PSBN_GBE_MASK | +- MTK_PHY_DA_RX_PSBN_LP_MASK, +- upper_idx << 12 | upper_idx << 8 | +- upper_idx << 4 | upper_idx); +- ret = upper_ret; +- } +- if (ret < 0) +- goto restore; +- +- /* We calibrate TX-VCM in different logic. Check upper index and then +- * lower index. If this calibration is valid, apply lower index's +- * result. +- */ +- ret = upper_ret - lower_ret; +- if (ret == 1) { +- ret = 0; +- /* Make sure we use upper_idx in our calibration system */ +- cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, +- MTK_PHY_DA_RX_PSBN_TBT_MASK | +- MTK_PHY_DA_RX_PSBN_HBT_MASK | +- MTK_PHY_DA_RX_PSBN_GBE_MASK | +- MTK_PHY_DA_RX_PSBN_LP_MASK, +- upper_idx << 12 | upper_idx << 8 | +- upper_idx << 4 | upper_idx); +- phydev_dbg(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx); +- } else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 && +- lower_ret == 1) { +- ret = 0; +- cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, +- MTK_PHY_DA_RX_PSBN_TBT_MASK | +- MTK_PHY_DA_RX_PSBN_HBT_MASK | +- MTK_PHY_DA_RX_PSBN_GBE_MASK | +- MTK_PHY_DA_RX_PSBN_LP_MASK, +- lower_idx << 12 | lower_idx << 8 | +- lower_idx << 4 | lower_idx); +- phydev_warn(phydev, "TX-VCM SW cal result at low margin 0x%x\n", +- lower_idx); +- } else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 && +- lower_ret == 0) { +- ret = 0; +- phydev_warn(phydev, +- "TX-VCM SW cal result at high margin 0x%x\n", +- upper_idx); +- } else { +- ret = -EINVAL; +- } +- +-restore: +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, +- MTK_PHY_RG_ANA_CALEN); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_TXVOS_CALEN); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, +- MTK_PHY_RG_ZCALEN_A); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_ZCALEN_B | MTK_PHY_RG_ZCALEN_C | +- MTK_PHY_RG_ZCALEN_D); +- +- return ret; +-} +- +-static void mt798x_phy_common_finetune(struct phy_device *phydev) +-{ +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ +- __phy_write(phydev, 0x11, 0xc71); +- __phy_write(phydev, 0x12, 0xc); +- __phy_write(phydev, 0x10, 0x8fae); +- +- /* EnabRandUpdTrig = 1 */ +- __phy_write(phydev, 0x11, 0x2f00); +- __phy_write(phydev, 0x12, 0xe); +- __phy_write(phydev, 0x10, 0x8fb0); +- +- /* NormMseLoThresh = 85 */ +- __phy_write(phydev, 0x11, 0x55a0); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x83aa); +- +- /* FfeUpdGainForce = 1(Enable), FfeUpdGainForceVal = 4 */ +- __phy_write(phydev, 0x11, 0x240); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9680); +- +- /* TrFreeze = 0 (mt7988 default) */ +- __phy_write(phydev, 0x11, 0x0); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9686); +- +- /* SSTrKp100 = 5 */ +- /* SSTrKf100 = 6 */ +- /* SSTrKp1000Mas = 5 */ +- /* SSTrKf1000Mas = 6 */ +- /* SSTrKp1000Slv = 5 */ +- /* SSTrKf1000Slv = 6 */ +- __phy_write(phydev, 0x11, 0xbaef); +- __phy_write(phydev, 0x12, 0x2e); +- __phy_write(phydev, 0x10, 0x968c); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +-} +- +-static void mt7981_phy_finetune(struct phy_device *phydev) +-{ +- u16 val[8] = { 0x01ce, 0x01c1, +- 0x020f, 0x0202, +- 0x03d0, 0x03c0, +- 0x0013, 0x0005 }; +- int i, k; +- +- /* 100M eye finetune: +- * Keep middle level of TX MLT3 shapper as default. +- * Only change TX MLT3 overshoot level here. +- */ +- for (k = 0, i = 1; i < 12; i++) { +- if (i % 3 == 0) +- continue; +- phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]); +- } +- +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* ResetSyncOffset = 6 */ +- __phy_write(phydev, 0x11, 0x600); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8fc0); +- +- /* VgaDecRate = 1 */ +- __phy_write(phydev, 0x11, 0x4c2a); +- __phy_write(phydev, 0x12, 0x3e); +- __phy_write(phydev, 0x10, 0x8fa4); +- +- /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, +- * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 +- */ +- __phy_write(phydev, 0x11, 0xd10a); +- __phy_write(phydev, 0x12, 0x34); +- __phy_write(phydev, 0x10, 0x8f82); +- +- /* VcoSlicerThreshBitsHigh */ +- __phy_write(phydev, 0x11, 0x5555); +- __phy_write(phydev, 0x12, 0x55); +- __phy_write(phydev, 0x10, 0x8ec0); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +- +- /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, +- MTK_PHY_TR_OPEN_LOOP_EN_MASK | +- MTK_PHY_LPF_X_AVERAGE_MASK, +- BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); +- +- /* rg_tr_lpf_cnt_val = 512 */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200); +- +- /* IIR2 related */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe); +- +- /* FFE peaking */ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C, +- MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D, +- MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e); +- +- /* Disable LDO pump */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRAB, 0x0); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRCD, 0x0); +- /* Adjust LDO output voltage */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222); +-} +- +-static void mt7988_phy_finetune(struct phy_device *phydev) +-{ +- u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182, +- 0x020d, 0x0206, 0x0384, 0x03d0, +- 0x03c6, 0x030a, 0x0011, 0x0005 }; +- int i; +- +- /* Set default MLT3 shaper first */ +- for (i = 0; i < 12; i++) +- phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[i]); +- +- /* TCT finetune */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); +- +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* ResetSyncOffset = 5 */ +- __phy_write(phydev, 0x11, 0x500); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8fc0); +- +- /* VgaDecRate is 1 at default on mt7988 */ +- +- /* MrvlTrFix100Kp = 6, MrvlTrFix100Kf = 7, +- * MrvlTrFix1000Kp = 6, MrvlTrFix1000Kf = 7 +- */ +- __phy_write(phydev, 0x11, 0xb90a); +- __phy_write(phydev, 0x12, 0x6f); +- __phy_write(phydev, 0x10, 0x8f82); +- +- /* RemAckCntLimitCtrl = 1 */ +- __phy_write(phydev, 0x11, 0xfbba); +- __phy_write(phydev, 0x12, 0xc3); +- __phy_write(phydev, 0x10, 0x87f8); +- +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +- +- /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, +- MTK_PHY_TR_OPEN_LOOP_EN_MASK | +- MTK_PHY_LPF_X_AVERAGE_MASK, +- BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0xa)); +- +- /* rg_tr_lpf_cnt_val = 1023 */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x3ff); +-} +- +-static void mt798x_phy_eee(struct phy_device *phydev) +-{ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120, +- MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK | +- MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, +- FIELD_PREP(MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK, 0x0) | +- FIELD_PREP(MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, 0x14)); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, +- MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, +- FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, +- 0xff)); +- +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_TESTMUX_ADC_CTRL, +- MTK_PHY_RG_TXEN_DIG_MASK); +- +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DEV1E_REG19b, MTK_PHY_BYPASS_DSP_LPI_READY); +- +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DEV1E_REG234, MTK_PHY_TR_LP_IIR_EEE_EN); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG238, +- MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK | +- MTK_PHY_LPI_SLV_SEND_TX_EN, +- FIELD_PREP(MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK, 0x120)); +- +- /* Keep MTK_PHY_LPI_SEND_LOC_TIMER as 375 */ +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239, +- MTK_PHY_LPI_TXPCS_LOC_RCV); +- +- /* This also fixes some IoT issues, such as CH340 */ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2C7, +- MTK_PHY_MAX_GAIN_MASK | MTK_PHY_MIN_GAIN_MASK, +- FIELD_PREP(MTK_PHY_MAX_GAIN_MASK, 0x8) | +- FIELD_PREP(MTK_PHY_MIN_GAIN_MASK, 0x13)); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2D1, +- MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, +- FIELD_PREP(MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, +- 0x33) | +- MTK_PHY_LPI_SKIP_SD_SLV_TR | MTK_PHY_LPI_TR_READY | +- MTK_PHY_LPI_VCO_EEE_STG0_EN); +- +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG323, +- MTK_PHY_EEE_WAKE_MAS_INT_DC | +- MTK_PHY_EEE_WAKE_SLV_INT_DC); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG324, +- MTK_PHY_SMI_DETCNT_MAX_MASK, +- FIELD_PREP(MTK_PHY_SMI_DETCNT_MAX_MASK, 0x3f) | +- MTK_PHY_SMI_DET_MAX_EN); +- +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG326, +- MTK_PHY_LPI_MODE_SD_ON | MTK_PHY_RESET_RANDUPD_CNT | +- MTK_PHY_TREC_UPDATE_ENAB_CLR | +- MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF | +- MTK_PHY_TR_READY_SKIP_AFE_WAKEUP); +- +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* Regsigdet_sel_1000 = 0 */ +- __phy_write(phydev, 0x11, 0xb); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9690); +- +- /* REG_EEE_st2TrKf1000 = 2 */ +- __phy_write(phydev, 0x11, 0x114f); +- __phy_write(phydev, 0x12, 0x2); +- __phy_write(phydev, 0x10, 0x969a); +- +- /* RegEEE_slv_wake_tr_timer_tar = 6, RegEEE_slv_remtx_timer_tar = 20 */ +- __phy_write(phydev, 0x11, 0x3028); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x969e); +- +- /* RegEEE_slv_wake_int_timer_tar = 8 */ +- __phy_write(phydev, 0x11, 0x5010); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96a0); +- +- /* RegEEE_trfreeze_timer2 = 586 */ +- __phy_write(phydev, 0x11, 0x24a); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96a8); +- +- /* RegEEE100Stg1_tar = 16 */ +- __phy_write(phydev, 0x11, 0x3210); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96b8); +- +- /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */ +- __phy_write(phydev, 0x11, 0x1463); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96ca); +- +- /* DfeTailEnableVgaThresh1000 = 27 */ +- __phy_write(phydev, 0x11, 0x36); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8f80); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +- +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3); +- __phy_modify(phydev, MTK_PHY_LPI_REG_14, +- MTK_PHY_LPI_WAKE_TIMER_1000_MASK, +- FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c)); +- +- __phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK, +- FIELD_PREP(MTK_PHY_SMI_DET_ON_THRESH_MASK, 0xc)); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, +- MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, +- FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, +- 0xff)); +-} +- +-static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item, +- u8 start_pair, u8 end_pair) +-{ +- u8 pair_n; +- int ret; +- +- for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { +- /* TX_OFFSET & TX_AMP have no SW calibration. */ +- switch (cal_item) { +- case TX_VCM: +- ret = tx_vcm_cal_sw(phydev, pair_n); +- break; +- default: +- return -EINVAL; +- } +- if (ret) +- return ret; +- } +- return 0; +-} +- +-static int cal_efuse(struct phy_device *phydev, enum CAL_ITEM cal_item, +- u8 start_pair, u8 end_pair, u32 *buf) +-{ +- u8 pair_n; +- int ret; +- +- for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { +- /* TX_VCM has no efuse calibration. */ +- switch (cal_item) { +- case REXT: +- ret = rext_cal_efuse(phydev, buf); +- break; +- case TX_OFFSET: +- ret = tx_offset_cal_efuse(phydev, buf); +- break; +- case TX_AMP: +- ret = tx_amp_cal_efuse(phydev, buf); +- break; +- case TX_R50: +- ret = tx_r50_cal_efuse(phydev, buf, pair_n); +- break; +- default: +- return -EINVAL; +- } +- if (ret) +- return ret; +- } +- +- return 0; +-} +- +-static int start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item, +- enum CAL_MODE cal_mode, u8 start_pair, +- u8 end_pair, u32 *buf) +-{ +- int ret; +- +- switch (cal_mode) { +- case EFUSE_M: +- ret = cal_efuse(phydev, cal_item, start_pair, +- end_pair, buf); +- break; +- case SW_M: +- ret = cal_sw(phydev, cal_item, start_pair, end_pair); +- break; +- default: +- return -EINVAL; +- } +- +- if (ret) { +- phydev_err(phydev, "cal %d failed\n", cal_item); +- return -EIO; +- } +- +- return 0; +-} +- +-static int mt798x_phy_calibration(struct phy_device *phydev) +-{ +- struct nvmem_cell *cell; +- int ret = 0; +- size_t len; +- u32 *buf; +- +- cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data"); +- if (IS_ERR(cell)) { +- if (PTR_ERR(cell) == -EPROBE_DEFER) +- return PTR_ERR(cell); +- return 0; +- } +- +- buf = (u32 *)nvmem_cell_read(cell, &len); +- nvmem_cell_put(cell); +- if (IS_ERR(buf)) +- return PTR_ERR(buf); +- +- if (!buf[0] || !buf[1] || !buf[2] || !buf[3] || len < 4 * sizeof(u32)) { +- phydev_err(phydev, "invalid efuse data\n"); +- ret = -EINVAL; +- goto out; +- } +- +- ret = start_cal(phydev, REXT, EFUSE_M, NO_PAIR, NO_PAIR, buf); +- if (ret) +- goto out; +- ret = start_cal(phydev, TX_OFFSET, EFUSE_M, NO_PAIR, NO_PAIR, buf); +- if (ret) +- goto out; +- ret = start_cal(phydev, TX_AMP, EFUSE_M, NO_PAIR, NO_PAIR, buf); +- if (ret) +- goto out; +- ret = start_cal(phydev, TX_R50, EFUSE_M, PAIR_A, PAIR_D, buf); +- if (ret) +- goto out; +- ret = start_cal(phydev, TX_VCM, SW_M, PAIR_A, PAIR_A, buf); +- if (ret) +- goto out; +- +-out: +- kfree(buf); +- return ret; +-} +- +-static int mt798x_phy_config_init(struct phy_device *phydev) +-{ +- switch (phydev->drv->phy_id) { +- case MTK_GPHY_ID_MT7981: +- mt7981_phy_finetune(phydev); +- break; +- case MTK_GPHY_ID_MT7988: +- mt7988_phy_finetune(phydev); +- break; +- } +- +- mt798x_phy_common_finetune(phydev); +- mt798x_phy_eee(phydev); +- +- return mt798x_phy_calibration(phydev); +-} +- +-static int mt798x_phy_hw_led_on_set(struct phy_device *phydev, u8 index, +- bool on) +-{ +- unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- bool changed; +- +- if (on) +- changed = !test_and_set_bit(bit_on, &priv->led_state); +- else +- changed = !!test_and_clear_bit(bit_on, &priv->led_state); +- +- changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV + +- (index ? 16 : 0), &priv->led_state); +- if (changed) +- return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : +- MTK_PHY_LED0_ON_CTRL, +- MTK_PHY_LED_ON_MASK, +- on ? MTK_PHY_LED_ON_FORCE_ON : 0); +- else +- return 0; +-} +- +-static int mt798x_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, +- bool blinking) +-{ +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + +- (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- bool changed; +- +- if (blinking) +- changed = !test_and_set_bit(bit_blink, &priv->led_state); +- else +- changed = !!test_and_clear_bit(bit_blink, &priv->led_state); +- +- changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV + +- (index ? 16 : 0), &priv->led_state); +- if (changed) +- return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL, +- blinking ? +- MTK_PHY_LED_BLINK_FORCE_BLINK : 0); +- else +- return 0; +-} +- +-static int mt798x_phy_led_blink_set(struct phy_device *phydev, u8 index, +- unsigned long *delay_on, +- unsigned long *delay_off) +-{ +- bool blinking = false; +- int err = 0; +- +- if (index > 1) +- return -EINVAL; +- +- if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { +- blinking = true; +- *delay_on = 50; +- *delay_off = 50; +- } +- +- err = mt798x_phy_hw_led_blink_set(phydev, index, blinking); +- if (err) +- return err; +- +- return mt798x_phy_hw_led_on_set(phydev, index, false); +-} +- +-static int mt798x_phy_led_brightness_set(struct phy_device *phydev, +- u8 index, enum led_brightness value) +-{ +- int err; +- +- err = mt798x_phy_hw_led_blink_set(phydev, index, false); +- if (err) +- return err; +- +- return mt798x_phy_hw_led_on_set(phydev, index, (value != LED_OFF)); +-} +- +-static const unsigned long supported_triggers = +- BIT(TRIGGER_NETDEV_FULL_DUPLEX) | +- BIT(TRIGGER_NETDEV_HALF_DUPLEX) | +- BIT(TRIGGER_NETDEV_LINK) | +- BIT(TRIGGER_NETDEV_LINK_10) | +- BIT(TRIGGER_NETDEV_LINK_100) | +- BIT(TRIGGER_NETDEV_LINK_1000) | +- BIT(TRIGGER_NETDEV_RX) | +- BIT(TRIGGER_NETDEV_TX); +- +-static int mt798x_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- if (index > 1) +- return -EINVAL; +- +- /* All combinations of the supported triggers are allowed */ +- if (rules & ~supported_triggers) +- return -EOPNOTSUPP; +- +- return 0; +-}; +- +-static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, +- unsigned long *rules) +-{ +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + +- (index ? 16 : 0); +- unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); +- unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- int on, blink; +- +- if (index > 1) +- return -EINVAL; +- +- on = phy_read_mmd(phydev, MDIO_MMD_VEND2, +- index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL); +- +- if (on < 0) +- return -EIO; +- +- blink = phy_read_mmd(phydev, MDIO_MMD_VEND2, +- index ? MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL); +- if (blink < 0) +- return -EIO; +- +- if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | +- MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) || +- (blink & (MTK_PHY_LED_BLINK_RX | MTK_PHY_LED_BLINK_TX))) +- set_bit(bit_netdev, &priv->led_state); +- else +- clear_bit(bit_netdev, &priv->led_state); +- +- if (on & MTK_PHY_LED_ON_FORCE_ON) +- set_bit(bit_on, &priv->led_state); +- else +- clear_bit(bit_on, &priv->led_state); +- +- if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK) +- set_bit(bit_blink, &priv->led_state); +- else +- clear_bit(bit_blink, &priv->led_state); +- +- if (!rules) +- return 0; +- +- if (on & MTK_PHY_LED_ON_LINK) +- *rules |= BIT(TRIGGER_NETDEV_LINK); +- +- if (on & MTK_PHY_LED_ON_LINK10) +- *rules |= BIT(TRIGGER_NETDEV_LINK_10); +- +- if (on & MTK_PHY_LED_ON_LINK100) +- *rules |= BIT(TRIGGER_NETDEV_LINK_100); +- +- if (on & MTK_PHY_LED_ON_LINK1000) +- *rules |= BIT(TRIGGER_NETDEV_LINK_1000); +- +- if (on & MTK_PHY_LED_ON_FDX) +- *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX); +- +- if (on & MTK_PHY_LED_ON_HDX) +- *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX); +- +- if (blink & MTK_PHY_LED_BLINK_RX) +- *rules |= BIT(TRIGGER_NETDEV_RX); +- +- if (blink & MTK_PHY_LED_BLINK_TX) +- *rules |= BIT(TRIGGER_NETDEV_TX); +- +- return 0; +-}; +- +-static int mt798x_phy_led_hw_control_set(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- u16 on = 0, blink = 0; +- int ret; +- +- if (index > 1) +- return -EINVAL; +- +- if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) +- on |= MTK_PHY_LED_ON_FDX; +- +- if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) +- on |= MTK_PHY_LED_ON_HDX; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK10; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK100; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK1000; +- +- if (rules & BIT(TRIGGER_NETDEV_RX)) { +- blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? +- MTK_PHY_LED_BLINK_10RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? +- MTK_PHY_LED_BLINK_100RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? +- MTK_PHY_LED_BLINK_1000RX : 0)) : +- MTK_PHY_LED_BLINK_RX; +- } +- +- if (rules & BIT(TRIGGER_NETDEV_TX)) { +- blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? +- MTK_PHY_LED_BLINK_10TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? +- MTK_PHY_LED_BLINK_100TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? +- MTK_PHY_LED_BLINK_1000TX : 0)) : +- MTK_PHY_LED_BLINK_TX; +- } +- +- if (blink || on) +- set_bit(bit_netdev, &priv->led_state); +- else +- clear_bit(bit_netdev, &priv->led_state); +- +- ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : +- MTK_PHY_LED0_ON_CTRL, +- MTK_PHY_LED_ON_FDX | +- MTK_PHY_LED_ON_HDX | +- MTK_PHY_LED_ON_LINK, +- on); +- +- if (ret) +- return ret; +- +- return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL, blink); +-}; +- +-static bool mt7988_phy_led_get_polarity(struct phy_device *phydev, int led_num) +-{ +- struct mtk_socphy_shared *priv = phydev->shared->priv; +- u32 polarities; +- +- if (led_num == 0) +- polarities = ~(priv->boottrap); +- else +- polarities = MTK_PHY_LED1_DEFAULT_POLARITIES; +- +- if (polarities & BIT(phydev->mdio.addr)) +- return true; +- +- return false; +-} +- +-static int mt7988_phy_fix_leds_polarities(struct phy_device *phydev) +-{ +- struct pinctrl *pinctrl; +- int index; +- +- /* Setup LED polarity according to bootstrap use of LED pins */ +- for (index = 0; index < 2; ++index) +- phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL, +- MTK_PHY_LED_ON_POLARITY, +- mt7988_phy_led_get_polarity(phydev, index) ? +- MTK_PHY_LED_ON_POLARITY : 0); +- +- /* Only now setup pinctrl to avoid bogus blinking */ +- pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led"); +- if (IS_ERR(pinctrl)) +- dev_err(&phydev->mdio.bus->dev, +- "Failed to setup PHY LED pinctrl\n"); +- +- return 0; +-} +- +-static int mt7988_phy_probe_shared(struct phy_device *phydev) +-{ +- struct device_node *np = dev_of_node(&phydev->mdio.bus->dev); +- struct mtk_socphy_shared *shared = phydev->shared->priv; +- struct regmap *regmap; +- u32 reg; +- int ret; +- +- /* The LED0 of the 4 PHYs in MT7988 are wired to SoC pins LED_A, LED_B, +- * LED_C and LED_D respectively. At the same time those pins are used to +- * bootstrap configuration of the reference clock source (LED_A), +- * DRAM DDRx16b x2/x1 (LED_B) and boot device (LED_C, LED_D). +- * In practice this is done using a LED and a resistor pulling the pin +- * either to GND or to VIO. +- * The detected value at boot time is accessible at run-time using the +- * TPBANK0 register located in the gpio base of the pinctrl, in order +- * to read it here it needs to be referenced by a phandle called +- * 'mediatek,pio' in the MDIO bus hosting the PHY. +- * The 4 bits in TPBANK0 are kept as package shared data and are used to +- * set LED polarity for each of the LED0. +- */ +- regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,pio"); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- ret = regmap_read(regmap, RG_GPIO_MISC_TPBANK0, ®); +- if (ret) +- return ret; +- +- shared->boottrap = FIELD_GET(RG_GPIO_MISC_TPBANK0_BOOTMODE, reg); +- +- return 0; +-} +- +-static void mt798x_phy_leds_state_init(struct phy_device *phydev) +-{ +- int i; +- +- for (i = 0; i < 2; ++i) +- mt798x_phy_led_hw_control_get(phydev, i, NULL); +-} +- +-static int mt7988_phy_probe(struct phy_device *phydev) +-{ +- struct mtk_socphy_shared *shared; +- struct mtk_socphy_priv *priv; +- int err; +- +- if (phydev->mdio.addr > 3) +- return -EINVAL; +- +- err = devm_phy_package_join(&phydev->mdio.dev, phydev, 0, +- sizeof(struct mtk_socphy_shared)); +- if (err) +- return err; +- +- if (phy_package_probe_once(phydev)) { +- err = mt7988_phy_probe_shared(phydev); +- if (err) +- return err; +- } +- +- shared = phydev->shared->priv; +- priv = &shared->priv[phydev->mdio.addr]; +- +- phydev->priv = priv; +- +- mt798x_phy_leds_state_init(phydev); +- +- err = mt7988_phy_fix_leds_polarities(phydev); +- if (err) +- return err; +- +- /* Disable TX power saving at probing to: +- * 1. Meet common mode compliance test criteria +- * 2. Make sure that TX-VCM calibration works fine +- */ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, +- MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); +- +- return mt798x_phy_calibration(phydev); +-} +- +-static int mt7981_phy_probe(struct phy_device *phydev) +-{ +- struct mtk_socphy_priv *priv; +- +- priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct mtk_socphy_priv), +- GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- phydev->priv = priv; +- +- mt798x_phy_leds_state_init(phydev); +- +- return mt798x_phy_calibration(phydev); +-} +- +-static struct phy_driver mtk_socphy_driver[] = { +- { +- PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981), +- .name = "MediaTek MT7981 PHY", +- .config_init = mt798x_phy_config_init, +- .config_intr = genphy_no_config_intr, +- .handle_interrupt = genphy_handle_interrupt_no_ack, +- .probe = mt7981_phy_probe, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = mtk_socphy_read_page, +- .write_page = mtk_socphy_write_page, +- .led_blink_set = mt798x_phy_led_blink_set, +- .led_brightness_set = mt798x_phy_led_brightness_set, +- .led_hw_is_supported = mt798x_phy_led_hw_is_supported, +- .led_hw_control_set = mt798x_phy_led_hw_control_set, +- .led_hw_control_get = mt798x_phy_led_hw_control_get, +- }, +- { +- PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988), +- .name = "MediaTek MT7988 PHY", +- .config_init = mt798x_phy_config_init, +- .config_intr = genphy_no_config_intr, +- .handle_interrupt = genphy_handle_interrupt_no_ack, +- .probe = mt7988_phy_probe, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = mtk_socphy_read_page, +- .write_page = mtk_socphy_write_page, +- .led_blink_set = mt798x_phy_led_blink_set, +- .led_brightness_set = mt798x_phy_led_brightness_set, +- .led_hw_is_supported = mt798x_phy_led_hw_is_supported, +- .led_hw_control_set = mt798x_phy_led_hw_control_set, +- .led_hw_control_get = mt798x_phy_led_hw_control_get, +- }, +-}; +- +-module_phy_driver(mtk_socphy_driver); +- +-static struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = { +- { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981) }, +- { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988) }, +- { } +-}; +- +-MODULE_DESCRIPTION("MediaTek SoC Gigabit Ethernet PHY driver"); +-MODULE_AUTHOR("Daniel Golle "); +-MODULE_AUTHOR("SkyLake Huang "); +-MODULE_LICENSE("GPL"); +- +-MODULE_DEVICE_TABLE(mdio, mtk_socphy_tbl); +--- a/drivers/net/phy/mediatek-ge.c ++++ /dev/null +@@ -1,111 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-#include +-#include +-#include +- +-#define MTK_EXT_PAGE_ACCESS 0x1f +-#define MTK_PHY_PAGE_STANDARD 0x0000 +-#define MTK_PHY_PAGE_EXTENDED 0x0001 +-#define MTK_PHY_PAGE_EXTENDED_2 0x0002 +-#define MTK_PHY_PAGE_EXTENDED_3 0x0003 +-#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +-#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 +- +-static int mtk_gephy_read_page(struct phy_device *phydev) +-{ +- return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +-} +- +-static int mtk_gephy_write_page(struct phy_device *phydev, int page) +-{ +- return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +-} +- +-static void mtk_gephy_config_init(struct phy_device *phydev) +-{ +- /* Enable HW auto downshift */ +- phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4)); +- +- /* Increase SlvDPSready time */ +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- __phy_write(phydev, 0x10, 0xafae); +- __phy_write(phydev, 0x12, 0x2f); +- __phy_write(phydev, 0x10, 0x8fae); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +- +- /* Adjust 100_mse_threshold */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff); +- +- /* Disable mcc */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300); +-} +- +-static int mt7530_phy_config_init(struct phy_device *phydev) +-{ +- mtk_gephy_config_init(phydev); +- +- /* Increase post_update_timer */ +- phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b); +- +- return 0; +-} +- +-static int mt7531_phy_config_init(struct phy_device *phydev) +-{ +- mtk_gephy_config_init(phydev); +- +- /* PHY link down power saving enable */ +- phy_set_bits(phydev, 0x17, BIT(4)); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300); +- +- /* Set TX Pair delay selection */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); +- +- return 0; +-} +- +-static struct phy_driver mtk_gephy_driver[] = { +- { +- PHY_ID_MATCH_EXACT(0x03a29412), +- .name = "MediaTek MT7530 PHY", +- .config_init = mt7530_phy_config_init, +- /* Interrupts are handled by the switch, not the PHY +- * itself. +- */ +- .config_intr = genphy_no_config_intr, +- .handle_interrupt = genphy_handle_interrupt_no_ack, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = mtk_gephy_read_page, +- .write_page = mtk_gephy_write_page, +- }, +- { +- PHY_ID_MATCH_EXACT(0x03a29441), +- .name = "MediaTek MT7531 PHY", +- .config_init = mt7531_phy_config_init, +- /* Interrupts are handled by the switch, not the PHY +- * itself. +- */ +- .config_intr = genphy_no_config_intr, +- .handle_interrupt = genphy_handle_interrupt_no_ack, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = mtk_gephy_read_page, +- .write_page = mtk_gephy_write_page, +- }, +-}; +- +-module_phy_driver(mtk_gephy_driver); +- +-static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { +- { PHY_ID_MATCH_EXACT(0x03a29441) }, +- { PHY_ID_MATCH_EXACT(0x03a29412) }, +- { } +-}; +- +-MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver"); +-MODULE_AUTHOR("DENG, Qingfang "); +-MODULE_LICENSE("GPL"); +- +-MODULE_DEVICE_TABLE(mdio, mtk_gephy_tbl); +--- /dev/null ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -0,0 +1,1610 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MTK_GPHY_ID_MT7981 0x03a29461 ++#define MTK_GPHY_ID_MT7988 0x03a29481 ++ ++#define MTK_EXT_PAGE_ACCESS 0x1f ++#define MTK_PHY_PAGE_STANDARD 0x0000 ++#define MTK_PHY_PAGE_EXTENDED_3 0x0003 ++ ++#define MTK_PHY_LPI_REG_14 0x14 ++#define MTK_PHY_LPI_WAKE_TIMER_1000_MASK GENMASK(8, 0) ++ ++#define MTK_PHY_LPI_REG_1c 0x1c ++#define MTK_PHY_SMI_DET_ON_THRESH_MASK GENMASK(13, 8) ++ ++#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 ++#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 ++ ++#define ANALOG_INTERNAL_OPERATION_MAX_US 20 ++#define TXRESERVE_MIN 0 ++#define TXRESERVE_MAX 7 ++ ++#define MTK_PHY_ANARG_RG 0x10 ++#define MTK_PHY_TCLKOFFSET_MASK GENMASK(12, 8) ++ ++/* Registers on MDIO_MMD_VEND1 */ ++#define MTK_PHY_TXVLD_DA_RG 0x12 ++#define MTK_PHY_DA_TX_I2MPB_A_GBE_MASK GENMASK(15, 10) ++#define MTK_PHY_DA_TX_I2MPB_A_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_A2 0x16 ++#define MTK_PHY_DA_TX_I2MPB_A_HBT_MASK GENMASK(15, 10) ++#define MTK_PHY_DA_TX_I2MPB_A_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_B1 0x17 ++#define MTK_PHY_DA_TX_I2MPB_B_GBE_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_B_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_B2 0x18 ++#define MTK_PHY_DA_TX_I2MPB_B_HBT_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_B_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_C1 0x19 ++#define MTK_PHY_DA_TX_I2MPB_C_GBE_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_C_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_C2 0x20 ++#define MTK_PHY_DA_TX_I2MPB_C_HBT_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_C_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_D1 0x21 ++#define MTK_PHY_DA_TX_I2MPB_D_GBE_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_D_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_D2 0x22 ++#define MTK_PHY_DA_TX_I2MPB_D_HBT_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_D_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_RXADC_CTRL_RG7 0xc6 ++#define MTK_PHY_DA_AD_BUF_BIAS_LP_MASK GENMASK(9, 8) ++ ++#define MTK_PHY_RXADC_CTRL_RG9 0xc8 ++#define MTK_PHY_DA_RX_PSBN_TBT_MASK GENMASK(14, 12) ++#define MTK_PHY_DA_RX_PSBN_HBT_MASK GENMASK(10, 8) ++#define MTK_PHY_DA_RX_PSBN_GBE_MASK GENMASK(6, 4) ++#define MTK_PHY_DA_RX_PSBN_LP_MASK GENMASK(2, 0) ++ ++#define MTK_PHY_LDO_OUTPUT_V 0xd7 ++ ++#define MTK_PHY_RG_ANA_CAL_RG0 0xdb ++#define MTK_PHY_RG_CAL_CKINV BIT(12) ++#define MTK_PHY_RG_ANA_CALEN BIT(8) ++#define MTK_PHY_RG_ZCALEN_A BIT(0) ++ ++#define MTK_PHY_RG_ANA_CAL_RG1 0xdc ++#define MTK_PHY_RG_ZCALEN_B BIT(12) ++#define MTK_PHY_RG_ZCALEN_C BIT(8) ++#define MTK_PHY_RG_ZCALEN_D BIT(4) ++#define MTK_PHY_RG_TXVOS_CALEN BIT(0) ++ ++#define MTK_PHY_RG_ANA_CAL_RG5 0xe0 ++#define MTK_PHY_RG_REXT_TRIM_MASK GENMASK(13, 8) ++ ++#define MTK_PHY_RG_TX_FILTER 0xfe ++ ++#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120 0x120 ++#define MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK GENMASK(12, 8) ++#define MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK GENMASK(4, 0) ++ ++#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122 0x122 ++#define MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK GENMASK(7, 0) ++ ++#define MTK_PHY_RG_TESTMUX_ADC_CTRL 0x144 ++#define MTK_PHY_RG_TXEN_DIG_MASK GENMASK(5, 5) ++ ++#define MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B 0x172 ++#define MTK_PHY_CR_TX_AMP_OFFSET_A_MASK GENMASK(13, 8) ++#define MTK_PHY_CR_TX_AMP_OFFSET_B_MASK GENMASK(6, 0) ++ ++#define MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D 0x173 ++#define MTK_PHY_CR_TX_AMP_OFFSET_C_MASK GENMASK(13, 8) ++#define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0) ++ ++#define MTK_PHY_RG_AD_CAL_COMP 0x17a ++#define MTK_PHY_AD_CAL_COMP_OUT_MASK GENMASK(8, 8) ++ ++#define MTK_PHY_RG_AD_CAL_CLK 0x17b ++#define MTK_PHY_DA_CAL_CLK BIT(0) ++ ++#define MTK_PHY_RG_AD_CALIN 0x17c ++#define MTK_PHY_DA_CALIN_FLAG BIT(0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_A 0x17d ++#define MTK_PHY_DASN_DAC_IN0_A_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_B 0x17e ++#define MTK_PHY_DASN_DAC_IN0_B_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_C 0x17f ++#define MTK_PHY_DASN_DAC_IN0_C_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_D 0x180 ++#define MTK_PHY_DASN_DAC_IN0_D_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_A 0x181 ++#define MTK_PHY_DASN_DAC_IN1_A_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_B 0x182 ++#define MTK_PHY_DASN_DAC_IN1_B_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_C 0x183 ++#define MTK_PHY_DASN_DAC_IN1_C_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_D 0x184 ++#define MTK_PHY_DASN_DAC_IN1_D_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DEV1E_REG19b 0x19b ++#define MTK_PHY_BYPASS_DSP_LPI_READY BIT(8) ++ ++#define MTK_PHY_RG_LP_IIR2_K1_L 0x22a ++#define MTK_PHY_RG_LP_IIR2_K1_U 0x22b ++#define MTK_PHY_RG_LP_IIR2_K2_L 0x22c ++#define MTK_PHY_RG_LP_IIR2_K2_U 0x22d ++#define MTK_PHY_RG_LP_IIR2_K3_L 0x22e ++#define MTK_PHY_RG_LP_IIR2_K3_U 0x22f ++#define MTK_PHY_RG_LP_IIR2_K4_L 0x230 ++#define MTK_PHY_RG_LP_IIR2_K4_U 0x231 ++#define MTK_PHY_RG_LP_IIR2_K5_L 0x232 ++#define MTK_PHY_RG_LP_IIR2_K5_U 0x233 ++ ++#define MTK_PHY_RG_DEV1E_REG234 0x234 ++#define MTK_PHY_TR_OPEN_LOOP_EN_MASK GENMASK(0, 0) ++#define MTK_PHY_LPF_X_AVERAGE_MASK GENMASK(7, 4) ++#define MTK_PHY_TR_LP_IIR_EEE_EN BIT(12) ++ ++#define MTK_PHY_RG_LPF_CNT_VAL 0x235 ++ ++#define MTK_PHY_RG_DEV1E_REG238 0x238 ++#define MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK GENMASK(8, 0) ++#define MTK_PHY_LPI_SLV_SEND_TX_EN BIT(12) ++ ++#define MTK_PHY_RG_DEV1E_REG239 0x239 ++#define MTK_PHY_LPI_SEND_LOC_TIMER_MASK GENMASK(8, 0) ++#define MTK_PHY_LPI_TXPCS_LOC_RCV BIT(12) ++ ++#define MTK_PHY_RG_DEV1E_REG27C 0x27c ++#define MTK_PHY_VGASTATE_FFE_THR_ST1_MASK GENMASK(12, 8) ++#define MTK_PHY_RG_DEV1E_REG27D 0x27d ++#define MTK_PHY_VGASTATE_FFE_THR_ST2_MASK GENMASK(4, 0) ++ ++#define MTK_PHY_RG_DEV1E_REG2C7 0x2c7 ++#define MTK_PHY_MAX_GAIN_MASK GENMASK(4, 0) ++#define MTK_PHY_MIN_GAIN_MASK GENMASK(12, 8) ++ ++#define MTK_PHY_RG_DEV1E_REG2D1 0x2d1 ++#define MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK GENMASK(7, 0) ++#define MTK_PHY_LPI_SKIP_SD_SLV_TR BIT(8) ++#define MTK_PHY_LPI_TR_READY BIT(9) ++#define MTK_PHY_LPI_VCO_EEE_STG0_EN BIT(10) ++ ++#define MTK_PHY_RG_DEV1E_REG323 0x323 ++#define MTK_PHY_EEE_WAKE_MAS_INT_DC BIT(0) ++#define MTK_PHY_EEE_WAKE_SLV_INT_DC BIT(4) ++ ++#define MTK_PHY_RG_DEV1E_REG324 0x324 ++#define MTK_PHY_SMI_DETCNT_MAX_MASK GENMASK(5, 0) ++#define MTK_PHY_SMI_DET_MAX_EN BIT(8) ++ ++#define MTK_PHY_RG_DEV1E_REG326 0x326 ++#define MTK_PHY_LPI_MODE_SD_ON BIT(0) ++#define MTK_PHY_RESET_RANDUPD_CNT BIT(1) ++#define MTK_PHY_TREC_UPDATE_ENAB_CLR BIT(2) ++#define MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF BIT(4) ++#define MTK_PHY_TR_READY_SKIP_AFE_WAKEUP BIT(5) ++ ++#define MTK_PHY_LDO_PUMP_EN_PAIRAB 0x502 ++#define MTK_PHY_LDO_PUMP_EN_PAIRCD 0x503 ++ ++#define MTK_PHY_DA_TX_R50_PAIR_A 0x53d ++#define MTK_PHY_DA_TX_R50_PAIR_B 0x53e ++#define MTK_PHY_DA_TX_R50_PAIR_C 0x53f ++#define MTK_PHY_DA_TX_R50_PAIR_D 0x540 ++ ++/* Registers on MDIO_MMD_VEND2 */ ++#define MTK_PHY_LED0_ON_CTRL 0x24 ++#define MTK_PHY_LED1_ON_CTRL 0x26 ++#define MTK_PHY_LED_ON_MASK GENMASK(6, 0) ++#define MTK_PHY_LED_ON_LINK1000 BIT(0) ++#define MTK_PHY_LED_ON_LINK100 BIT(1) ++#define MTK_PHY_LED_ON_LINK10 BIT(2) ++#define MTK_PHY_LED_ON_LINK (MTK_PHY_LED_ON_LINK10 |\ ++ MTK_PHY_LED_ON_LINK100 |\ ++ MTK_PHY_LED_ON_LINK1000) ++#define MTK_PHY_LED_ON_LINKDOWN BIT(3) ++#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ ++#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ ++#define MTK_PHY_LED_ON_FORCE_ON BIT(6) ++#define MTK_PHY_LED_ON_POLARITY BIT(14) ++#define MTK_PHY_LED_ON_ENABLE BIT(15) ++ ++#define MTK_PHY_LED0_BLINK_CTRL 0x25 ++#define MTK_PHY_LED1_BLINK_CTRL 0x27 ++#define MTK_PHY_LED_BLINK_1000TX BIT(0) ++#define MTK_PHY_LED_BLINK_1000RX BIT(1) ++#define MTK_PHY_LED_BLINK_100TX BIT(2) ++#define MTK_PHY_LED_BLINK_100RX BIT(3) ++#define MTK_PHY_LED_BLINK_10TX BIT(4) ++#define MTK_PHY_LED_BLINK_10RX BIT(5) ++#define MTK_PHY_LED_BLINK_RX (MTK_PHY_LED_BLINK_10RX |\ ++ MTK_PHY_LED_BLINK_100RX |\ ++ MTK_PHY_LED_BLINK_1000RX) ++#define MTK_PHY_LED_BLINK_TX (MTK_PHY_LED_BLINK_10TX |\ ++ MTK_PHY_LED_BLINK_100TX |\ ++ MTK_PHY_LED_BLINK_1000TX) ++#define MTK_PHY_LED_BLINK_COLLISION BIT(6) ++#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) ++#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) ++#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9) ++ ++#define MTK_PHY_LED1_DEFAULT_POLARITIES BIT(1) ++ ++#define MTK_PHY_RG_BG_RASEL 0x115 ++#define MTK_PHY_RG_BG_RASEL_MASK GENMASK(2, 0) ++ ++/* 'boottrap' register reflecting the configuration of the 4 PHY LEDs */ ++#define RG_GPIO_MISC_TPBANK0 0x6f0 ++#define RG_GPIO_MISC_TPBANK0_BOOTMODE GENMASK(11, 8) ++ ++/* These macro privides efuse parsing for internal phy. */ ++#define EFS_DA_TX_I2MPB_A(x) (((x) >> 0) & GENMASK(5, 0)) ++#define EFS_DA_TX_I2MPB_B(x) (((x) >> 6) & GENMASK(5, 0)) ++#define EFS_DA_TX_I2MPB_C(x) (((x) >> 12) & GENMASK(5, 0)) ++#define EFS_DA_TX_I2MPB_D(x) (((x) >> 18) & GENMASK(5, 0)) ++#define EFS_DA_TX_AMP_OFFSET_A(x) (((x) >> 24) & GENMASK(5, 0)) ++ ++#define EFS_DA_TX_AMP_OFFSET_B(x) (((x) >> 0) & GENMASK(5, 0)) ++#define EFS_DA_TX_AMP_OFFSET_C(x) (((x) >> 6) & GENMASK(5, 0)) ++#define EFS_DA_TX_AMP_OFFSET_D(x) (((x) >> 12) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_A(x) (((x) >> 18) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_B(x) (((x) >> 24) & GENMASK(5, 0)) ++ ++#define EFS_DA_TX_R50_C(x) (((x) >> 0) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_D(x) (((x) >> 6) & GENMASK(5, 0)) ++ ++#define EFS_RG_BG_RASEL(x) (((x) >> 4) & GENMASK(2, 0)) ++#define EFS_RG_REXT_TRIM(x) (((x) >> 7) & GENMASK(5, 0)) ++ ++enum { ++ NO_PAIR, ++ PAIR_A, ++ PAIR_B, ++ PAIR_C, ++ PAIR_D, ++}; ++ ++enum calibration_mode { ++ EFUSE_K, ++ SW_K ++}; ++ ++enum CAL_ITEM { ++ REXT, ++ TX_OFFSET, ++ TX_AMP, ++ TX_R50, ++ TX_VCM ++}; ++ ++enum CAL_MODE { ++ EFUSE_M, ++ SW_M ++}; ++ ++#define MTK_PHY_LED_STATE_FORCE_ON 0 ++#define MTK_PHY_LED_STATE_FORCE_BLINK 1 ++#define MTK_PHY_LED_STATE_NETDEV 2 ++ ++struct mtk_socphy_priv { ++ unsigned long led_state; ++}; ++ ++struct mtk_socphy_shared { ++ u32 boottrap; ++ struct mtk_socphy_priv priv[4]; ++}; ++ ++static int mtk_socphy_read_page(struct phy_device *phydev) ++{ ++ return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); ++} ++ ++static int mtk_socphy_write_page(struct phy_device *phydev, int page) ++{ ++ return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); ++} ++ ++/* One calibration cycle consists of: ++ * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high ++ * until AD_CAL_COMP is ready to output calibration result. ++ * 2.Wait until DA_CAL_CLK is available. ++ * 3.Fetch AD_CAL_COMP_OUT. ++ */ ++static int cal_cycle(struct phy_device *phydev, int devad, ++ u32 regnum, u16 mask, u16 cal_val) ++{ ++ int reg_val; ++ int ret; ++ ++ phy_modify_mmd(phydev, devad, regnum, ++ mask, cal_val); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, ++ MTK_PHY_DA_CALIN_FLAG); ++ ++ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_AD_CAL_CLK, reg_val, ++ reg_val & MTK_PHY_DA_CAL_CLK, 500, ++ ANALOG_INTERNAL_OPERATION_MAX_US, ++ false); ++ if (ret) { ++ phydev_err(phydev, "Calibration cycle timeout\n"); ++ return ret; ++ } ++ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, ++ MTK_PHY_DA_CALIN_FLAG); ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP); ++ if (ret < 0) ++ return ret; ++ ret = FIELD_GET(MTK_PHY_AD_CAL_COMP_OUT_MASK, ret); ++ phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret); ++ ++ return ret; ++} ++ ++static int rext_fill_result(struct phy_device *phydev, u16 *buf) ++{ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_REXT_TRIM_MASK, buf[0] << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_BG_RASEL, ++ MTK_PHY_RG_BG_RASEL_MASK, buf[1]); ++ ++ return 0; ++} ++ ++static int rext_cal_efuse(struct phy_device *phydev, u32 *buf) ++{ ++ u16 rext_cal_val[2]; ++ ++ rext_cal_val[0] = EFS_RG_REXT_TRIM(buf[3]); ++ rext_cal_val[1] = EFS_RG_BG_RASEL(buf[3]); ++ rext_fill_result(phydev, rext_cal_val); ++ ++ return 0; ++} ++ ++static int tx_offset_fill_result(struct phy_device *phydev, u16 *buf) ++{ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, ++ MTK_PHY_CR_TX_AMP_OFFSET_A_MASK, buf[0] << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, ++ MTK_PHY_CR_TX_AMP_OFFSET_B_MASK, buf[1]); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, ++ MTK_PHY_CR_TX_AMP_OFFSET_C_MASK, buf[2] << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, ++ MTK_PHY_CR_TX_AMP_OFFSET_D_MASK, buf[3]); ++ ++ return 0; ++} ++ ++static int tx_offset_cal_efuse(struct phy_device *phydev, u32 *buf) ++{ ++ u16 tx_offset_cal_val[4]; ++ ++ tx_offset_cal_val[0] = EFS_DA_TX_AMP_OFFSET_A(buf[0]); ++ tx_offset_cal_val[1] = EFS_DA_TX_AMP_OFFSET_B(buf[1]); ++ tx_offset_cal_val[2] = EFS_DA_TX_AMP_OFFSET_C(buf[1]); ++ tx_offset_cal_val[3] = EFS_DA_TX_AMP_OFFSET_D(buf[1]); ++ ++ tx_offset_fill_result(phydev, tx_offset_cal_val); ++ ++ return 0; ++} ++ ++static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf) ++{ ++ const int vals_9481[16] = { 10, 6, 6, 10, ++ 10, 6, 6, 10, ++ 10, 6, 6, 10, ++ 10, 6, 6, 10 }; ++ const int vals_9461[16] = { 7, 1, 4, 7, ++ 7, 1, 4, 7, ++ 7, 1, 4, 7, ++ 7, 1, 4, 7 }; ++ int bias[16] = {}; ++ int i; ++ ++ switch (phydev->drv->phy_id) { ++ case MTK_GPHY_ID_MT7981: ++ /* We add some calibration to efuse values ++ * due to board level influence. ++ * GBE: +7, TBT: +1, HBT: +4, TST: +7 ++ */ ++ memcpy(bias, (const void *)vals_9461, sizeof(bias)); ++ break; ++ case MTK_GPHY_ID_MT7988: ++ memcpy(bias, (const void *)vals_9481, sizeof(bias)); ++ break; ++ } ++ ++ /* Prevent overflow */ ++ for (i = 0; i < 12; i++) { ++ if (buf[i >> 2] + bias[i] > 63) { ++ buf[i >> 2] = 63; ++ bias[i] = 0; ++ } ++ } ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, ++ MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, ++ buf[0] + bias[0])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, ++ MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, ++ buf[0] + bias[1])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, ++ MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, ++ buf[0] + bias[2])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, ++ MTK_PHY_DA_TX_I2MPB_A_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TST_MASK, ++ buf[0] + bias[3])); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, ++ MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, ++ buf[1] + bias[4])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, ++ MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, ++ buf[1] + bias[5])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, ++ MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, ++ buf[1] + bias[6])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, ++ MTK_PHY_DA_TX_I2MPB_B_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TST_MASK, ++ buf[1] + bias[7])); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, ++ MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, ++ buf[2] + bias[8])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, ++ MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, ++ buf[2] + bias[9])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, ++ MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, ++ buf[2] + bias[10])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, ++ MTK_PHY_DA_TX_I2MPB_C_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TST_MASK, ++ buf[2] + bias[11])); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, ++ MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, ++ buf[3] + bias[12])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, ++ MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, ++ buf[3] + bias[13])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, ++ MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, ++ buf[3] + bias[14])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, ++ MTK_PHY_DA_TX_I2MPB_D_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TST_MASK, ++ buf[3] + bias[15])); ++ ++ return 0; ++} ++ ++static int tx_amp_cal_efuse(struct phy_device *phydev, u32 *buf) ++{ ++ u16 tx_amp_cal_val[4]; ++ ++ tx_amp_cal_val[0] = EFS_DA_TX_I2MPB_A(buf[0]); ++ tx_amp_cal_val[1] = EFS_DA_TX_I2MPB_B(buf[0]); ++ tx_amp_cal_val[2] = EFS_DA_TX_I2MPB_C(buf[0]); ++ tx_amp_cal_val[3] = EFS_DA_TX_I2MPB_D(buf[0]); ++ tx_amp_fill_result(phydev, tx_amp_cal_val); ++ ++ return 0; ++} ++ ++static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val, ++ u8 txg_calen_x) ++{ ++ int bias = 0; ++ u16 reg, val; ++ ++ if (phydev->drv->phy_id == MTK_GPHY_ID_MT7988) ++ bias = -1; ++ ++ val = clamp_val(bias + tx_r50_cal_val, 0, 63); ++ ++ switch (txg_calen_x) { ++ case PAIR_A: ++ reg = MTK_PHY_DA_TX_R50_PAIR_A; ++ break; ++ case PAIR_B: ++ reg = MTK_PHY_DA_TX_R50_PAIR_B; ++ break; ++ case PAIR_C: ++ reg = MTK_PHY_DA_TX_R50_PAIR_C; ++ break; ++ case PAIR_D: ++ reg = MTK_PHY_DA_TX_R50_PAIR_D; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, val | val << 8); ++ ++ return 0; ++} ++ ++static int tx_r50_cal_efuse(struct phy_device *phydev, u32 *buf, ++ u8 txg_calen_x) ++{ ++ u16 tx_r50_cal_val; ++ ++ switch (txg_calen_x) { ++ case PAIR_A: ++ tx_r50_cal_val = EFS_DA_TX_R50_A(buf[1]); ++ break; ++ case PAIR_B: ++ tx_r50_cal_val = EFS_DA_TX_R50_B(buf[1]); ++ break; ++ case PAIR_C: ++ tx_r50_cal_val = EFS_DA_TX_R50_C(buf[2]); ++ break; ++ case PAIR_D: ++ tx_r50_cal_val = EFS_DA_TX_R50_D(buf[2]); ++ break; ++ default: ++ return -EINVAL; ++ } ++ tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); ++ ++ return 0; ++} ++ ++static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x) ++{ ++ u8 lower_idx, upper_idx, txreserve_val; ++ u8 lower_ret, upper_ret; ++ int ret; ++ ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ANA_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_CAL_CKINV); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_TXVOS_CALEN); ++ ++ switch (rg_txreserve_x) { ++ case PAIR_A: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_A, ++ MTK_PHY_DASN_DAC_IN0_A_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_A, ++ MTK_PHY_DASN_DAC_IN1_A_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ZCALEN_A); ++ break; ++ case PAIR_B: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_B, ++ MTK_PHY_DASN_DAC_IN0_B_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_B, ++ MTK_PHY_DASN_DAC_IN1_B_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_B); ++ break; ++ case PAIR_C: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_C, ++ MTK_PHY_DASN_DAC_IN0_C_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_C, ++ MTK_PHY_DASN_DAC_IN1_C_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_C); ++ break; ++ case PAIR_D: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_D, ++ MTK_PHY_DASN_DAC_IN0_D_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_D, ++ MTK_PHY_DASN_DAC_IN1_D_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_D); ++ break; ++ default: ++ ret = -EINVAL; ++ goto restore; ++ } ++ ++ lower_idx = TXRESERVE_MIN; ++ upper_idx = TXRESERVE_MAX; ++ ++ phydev_dbg(phydev, "Start TX-VCM SW cal.\n"); ++ while ((upper_idx - lower_idx) > 1) { ++ txreserve_val = DIV_ROUND_CLOSEST(lower_idx + upper_idx, 2); ++ ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ txreserve_val << 12 | txreserve_val << 8 | ++ txreserve_val << 4 | txreserve_val); ++ if (ret == 1) { ++ upper_idx = txreserve_val; ++ upper_ret = ret; ++ } else if (ret == 0) { ++ lower_idx = txreserve_val; ++ lower_ret = ret; ++ } else { ++ goto restore; ++ } ++ } ++ ++ if (lower_idx == TXRESERVE_MIN) { ++ lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ lower_idx << 12 | lower_idx << 8 | ++ lower_idx << 4 | lower_idx); ++ ret = lower_ret; ++ } else if (upper_idx == TXRESERVE_MAX) { ++ upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ upper_idx << 12 | upper_idx << 8 | ++ upper_idx << 4 | upper_idx); ++ ret = upper_ret; ++ } ++ if (ret < 0) ++ goto restore; ++ ++ /* We calibrate TX-VCM in different logic. Check upper index and then ++ * lower index. If this calibration is valid, apply lower index's ++ * result. ++ */ ++ ret = upper_ret - lower_ret; ++ if (ret == 1) { ++ ret = 0; ++ /* Make sure we use upper_idx in our calibration system */ ++ cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ upper_idx << 12 | upper_idx << 8 | ++ upper_idx << 4 | upper_idx); ++ phydev_dbg(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx); ++ } else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 && ++ lower_ret == 1) { ++ ret = 0; ++ cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ lower_idx << 12 | lower_idx << 8 | ++ lower_idx << 4 | lower_idx); ++ phydev_warn(phydev, "TX-VCM SW cal result at low margin 0x%x\n", ++ lower_idx); ++ } else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 && ++ lower_ret == 0) { ++ ret = 0; ++ phydev_warn(phydev, ++ "TX-VCM SW cal result at high margin 0x%x\n", ++ upper_idx); ++ } else { ++ ret = -EINVAL; ++ } ++ ++restore: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ANA_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_TXVOS_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ZCALEN_A); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_B | MTK_PHY_RG_ZCALEN_C | ++ MTK_PHY_RG_ZCALEN_D); ++ ++ return ret; ++} ++ ++static void mt798x_phy_common_finetune(struct phy_device *phydev) ++{ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ ++ __phy_write(phydev, 0x11, 0xc71); ++ __phy_write(phydev, 0x12, 0xc); ++ __phy_write(phydev, 0x10, 0x8fae); ++ ++ /* EnabRandUpdTrig = 1 */ ++ __phy_write(phydev, 0x11, 0x2f00); ++ __phy_write(phydev, 0x12, 0xe); ++ __phy_write(phydev, 0x10, 0x8fb0); ++ ++ /* NormMseLoThresh = 85 */ ++ __phy_write(phydev, 0x11, 0x55a0); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x83aa); ++ ++ /* FfeUpdGainForce = 1(Enable), FfeUpdGainForceVal = 4 */ ++ __phy_write(phydev, 0x11, 0x240); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x9680); ++ ++ /* TrFreeze = 0 (mt7988 default) */ ++ __phy_write(phydev, 0x11, 0x0); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x9686); ++ ++ /* SSTrKp100 = 5 */ ++ /* SSTrKf100 = 6 */ ++ /* SSTrKp1000Mas = 5 */ ++ /* SSTrKf1000Mas = 6 */ ++ /* SSTrKp1000Slv = 5 */ ++ /* SSTrKf1000Slv = 6 */ ++ __phy_write(phydev, 0x11, 0xbaef); ++ __phy_write(phydev, 0x12, 0x2e); ++ __phy_write(phydev, 0x10, 0x968c); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++} ++ ++static void mt7981_phy_finetune(struct phy_device *phydev) ++{ ++ u16 val[8] = { 0x01ce, 0x01c1, ++ 0x020f, 0x0202, ++ 0x03d0, 0x03c0, ++ 0x0013, 0x0005 }; ++ int i, k; ++ ++ /* 100M eye finetune: ++ * Keep middle level of TX MLT3 shapper as default. ++ * Only change TX MLT3 overshoot level here. ++ */ ++ for (k = 0, i = 1; i < 12; i++) { ++ if (i % 3 == 0) ++ continue; ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]); ++ } ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ /* ResetSyncOffset = 6 */ ++ __phy_write(phydev, 0x11, 0x600); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x8fc0); ++ ++ /* VgaDecRate = 1 */ ++ __phy_write(phydev, 0x11, 0x4c2a); ++ __phy_write(phydev, 0x12, 0x3e); ++ __phy_write(phydev, 0x10, 0x8fa4); ++ ++ /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, ++ * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 ++ */ ++ __phy_write(phydev, 0x11, 0xd10a); ++ __phy_write(phydev, 0x12, 0x34); ++ __phy_write(phydev, 0x10, 0x8f82); ++ ++ /* VcoSlicerThreshBitsHigh */ ++ __phy_write(phydev, 0x11, 0x5555); ++ __phy_write(phydev, 0x12, 0x55); ++ __phy_write(phydev, 0x10, 0x8ec0); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, ++ MTK_PHY_TR_OPEN_LOOP_EN_MASK | ++ MTK_PHY_LPF_X_AVERAGE_MASK, ++ BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); ++ ++ /* rg_tr_lpf_cnt_val = 512 */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200); ++ ++ /* IIR2 related */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe); ++ ++ /* FFE peaking */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C, ++ MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D, ++ MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e); ++ ++ /* Disable LDO pump */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRAB, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRCD, 0x0); ++ /* Adjust LDO output voltage */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222); ++} ++ ++static void mt7988_phy_finetune(struct phy_device *phydev) ++{ ++ u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182, ++ 0x020d, 0x0206, 0x0384, 0x03d0, ++ 0x03c6, 0x030a, 0x0011, 0x0005 }; ++ int i; ++ ++ /* Set default MLT3 shaper first */ ++ for (i = 0; i < 12; i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[i]); ++ ++ /* TCT finetune */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ /* ResetSyncOffset = 5 */ ++ __phy_write(phydev, 0x11, 0x500); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x8fc0); ++ ++ /* VgaDecRate is 1 at default on mt7988 */ ++ ++ /* MrvlTrFix100Kp = 6, MrvlTrFix100Kf = 7, ++ * MrvlTrFix1000Kp = 6, MrvlTrFix1000Kf = 7 ++ */ ++ __phy_write(phydev, 0x11, 0xb90a); ++ __phy_write(phydev, 0x12, 0x6f); ++ __phy_write(phydev, 0x10, 0x8f82); ++ ++ /* RemAckCntLimitCtrl = 1 */ ++ __phy_write(phydev, 0x11, 0xfbba); ++ __phy_write(phydev, 0x12, 0xc3); ++ __phy_write(phydev, 0x10, 0x87f8); ++ ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, ++ MTK_PHY_TR_OPEN_LOOP_EN_MASK | ++ MTK_PHY_LPF_X_AVERAGE_MASK, ++ BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0xa)); ++ ++ /* rg_tr_lpf_cnt_val = 1023 */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x3ff); ++} ++ ++static void mt798x_phy_eee(struct phy_device *phydev) ++{ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120, ++ MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK | ++ MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, ++ FIELD_PREP(MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK, 0x0) | ++ FIELD_PREP(MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, 0x14)); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, ++ MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, ++ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, ++ 0xff)); ++ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_TESTMUX_ADC_CTRL, ++ MTK_PHY_RG_TXEN_DIG_MASK); ++ ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DEV1E_REG19b, MTK_PHY_BYPASS_DSP_LPI_READY); ++ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DEV1E_REG234, MTK_PHY_TR_LP_IIR_EEE_EN); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG238, ++ MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK | ++ MTK_PHY_LPI_SLV_SEND_TX_EN, ++ FIELD_PREP(MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK, 0x120)); ++ ++ /* Keep MTK_PHY_LPI_SEND_LOC_TIMER as 375 */ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239, ++ MTK_PHY_LPI_TXPCS_LOC_RCV); ++ ++ /* This also fixes some IoT issues, such as CH340 */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2C7, ++ MTK_PHY_MAX_GAIN_MASK | MTK_PHY_MIN_GAIN_MASK, ++ FIELD_PREP(MTK_PHY_MAX_GAIN_MASK, 0x8) | ++ FIELD_PREP(MTK_PHY_MIN_GAIN_MASK, 0x13)); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2D1, ++ MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, ++ FIELD_PREP(MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, ++ 0x33) | ++ MTK_PHY_LPI_SKIP_SD_SLV_TR | MTK_PHY_LPI_TR_READY | ++ MTK_PHY_LPI_VCO_EEE_STG0_EN); ++ ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG323, ++ MTK_PHY_EEE_WAKE_MAS_INT_DC | ++ MTK_PHY_EEE_WAKE_SLV_INT_DC); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG324, ++ MTK_PHY_SMI_DETCNT_MAX_MASK, ++ FIELD_PREP(MTK_PHY_SMI_DETCNT_MAX_MASK, 0x3f) | ++ MTK_PHY_SMI_DET_MAX_EN); ++ ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG326, ++ MTK_PHY_LPI_MODE_SD_ON | MTK_PHY_RESET_RANDUPD_CNT | ++ MTK_PHY_TREC_UPDATE_ENAB_CLR | ++ MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF | ++ MTK_PHY_TR_READY_SKIP_AFE_WAKEUP); ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ /* Regsigdet_sel_1000 = 0 */ ++ __phy_write(phydev, 0x11, 0xb); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x9690); ++ ++ /* REG_EEE_st2TrKf1000 = 2 */ ++ __phy_write(phydev, 0x11, 0x114f); ++ __phy_write(phydev, 0x12, 0x2); ++ __phy_write(phydev, 0x10, 0x969a); ++ ++ /* RegEEE_slv_wake_tr_timer_tar = 6, RegEEE_slv_remtx_timer_tar = 20 */ ++ __phy_write(phydev, 0x11, 0x3028); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x969e); ++ ++ /* RegEEE_slv_wake_int_timer_tar = 8 */ ++ __phy_write(phydev, 0x11, 0x5010); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x96a0); ++ ++ /* RegEEE_trfreeze_timer2 = 586 */ ++ __phy_write(phydev, 0x11, 0x24a); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x96a8); ++ ++ /* RegEEE100Stg1_tar = 16 */ ++ __phy_write(phydev, 0x11, 0x3210); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x96b8); ++ ++ /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */ ++ __phy_write(phydev, 0x11, 0x1463); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x96ca); ++ ++ /* DfeTailEnableVgaThresh1000 = 27 */ ++ __phy_write(phydev, 0x11, 0x36); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x8f80); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3); ++ __phy_modify(phydev, MTK_PHY_LPI_REG_14, ++ MTK_PHY_LPI_WAKE_TIMER_1000_MASK, ++ FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c)); ++ ++ __phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK, ++ FIELD_PREP(MTK_PHY_SMI_DET_ON_THRESH_MASK, 0xc)); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, ++ MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, ++ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, ++ 0xff)); ++} ++ ++static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item, ++ u8 start_pair, u8 end_pair) ++{ ++ u8 pair_n; ++ int ret; ++ ++ for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { ++ /* TX_OFFSET & TX_AMP have no SW calibration. */ ++ switch (cal_item) { ++ case TX_VCM: ++ ret = tx_vcm_cal_sw(phydev, pair_n); ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ ++static int cal_efuse(struct phy_device *phydev, enum CAL_ITEM cal_item, ++ u8 start_pair, u8 end_pair, u32 *buf) ++{ ++ u8 pair_n; ++ int ret; ++ ++ for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { ++ /* TX_VCM has no efuse calibration. */ ++ switch (cal_item) { ++ case REXT: ++ ret = rext_cal_efuse(phydev, buf); ++ break; ++ case TX_OFFSET: ++ ret = tx_offset_cal_efuse(phydev, buf); ++ break; ++ case TX_AMP: ++ ret = tx_amp_cal_efuse(phydev, buf); ++ break; ++ case TX_R50: ++ ret = tx_r50_cal_efuse(phydev, buf, pair_n); ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item, ++ enum CAL_MODE cal_mode, u8 start_pair, ++ u8 end_pair, u32 *buf) ++{ ++ int ret; ++ ++ switch (cal_mode) { ++ case EFUSE_M: ++ ret = cal_efuse(phydev, cal_item, start_pair, ++ end_pair, buf); ++ break; ++ case SW_M: ++ ret = cal_sw(phydev, cal_item, start_pair, end_pair); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (ret) { ++ phydev_err(phydev, "cal %d failed\n", cal_item); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int mt798x_phy_calibration(struct phy_device *phydev) ++{ ++ struct nvmem_cell *cell; ++ int ret = 0; ++ size_t len; ++ u32 *buf; ++ ++ cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data"); ++ if (IS_ERR(cell)) { ++ if (PTR_ERR(cell) == -EPROBE_DEFER) ++ return PTR_ERR(cell); ++ return 0; ++ } ++ ++ buf = (u32 *)nvmem_cell_read(cell, &len); ++ if (IS_ERR(buf)) ++ return PTR_ERR(buf); ++ nvmem_cell_put(cell); ++ ++ if (!buf[0] || !buf[1] || !buf[2] || !buf[3] || len < 4 * sizeof(u32)) { ++ phydev_err(phydev, "invalid efuse data\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = start_cal(phydev, REXT, EFUSE_M, NO_PAIR, NO_PAIR, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_OFFSET, EFUSE_M, NO_PAIR, NO_PAIR, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_AMP, EFUSE_M, NO_PAIR, NO_PAIR, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_R50, EFUSE_M, PAIR_A, PAIR_D, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_VCM, SW_M, PAIR_A, PAIR_A, buf); ++ if (ret) ++ goto out; ++ ++out: ++ kfree(buf); ++ return ret; ++} ++ ++static int mt798x_phy_config_init(struct phy_device *phydev) ++{ ++ switch (phydev->drv->phy_id) { ++ case MTK_GPHY_ID_MT7981: ++ mt7981_phy_finetune(phydev); ++ break; ++ case MTK_GPHY_ID_MT7988: ++ mt7988_phy_finetune(phydev); ++ break; ++ } ++ ++ mt798x_phy_common_finetune(phydev); ++ mt798x_phy_eee(phydev); ++ ++ return mt798x_phy_calibration(phydev); ++} ++ ++static int mt798x_phy_hw_led_on_set(struct phy_device *phydev, u8 index, ++ bool on) ++{ ++ unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (on) ++ changed = !test_and_set_bit(bit_on, &priv->led_state); ++ else ++ changed = !!test_and_clear_bit(bit_on, &priv->led_state); ++ ++ changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV + ++ (index ? 16 : 0), &priv->led_state); ++ if (changed) ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : ++ MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED_ON_MASK, ++ on ? MTK_PHY_LED_ON_FORCE_ON : 0); ++ else ++ return 0; ++} ++ ++static int mt798x_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, ++ bool blinking) ++{ ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (blinking) ++ changed = !test_and_set_bit(bit_blink, &priv->led_state); ++ else ++ changed = !!test_and_clear_bit(bit_blink, &priv->led_state); ++ ++ changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV + ++ (index ? 16 : 0), &priv->led_state); ++ if (changed) ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL, ++ blinking ? ++ MTK_PHY_LED_BLINK_FORCE_BLINK : 0); ++ else ++ return 0; ++} ++ ++static int mt798x_phy_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ bool blinking = false; ++ int err = 0; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { ++ blinking = true; ++ *delay_on = 50; ++ *delay_off = 50; ++ } ++ ++ err = mt798x_phy_hw_led_blink_set(phydev, index, blinking); ++ if (err) ++ return err; ++ ++ return mt798x_phy_hw_led_on_set(phydev, index, false); ++} ++ ++static int mt798x_phy_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ int err; ++ ++ err = mt798x_phy_hw_led_blink_set(phydev, index, false); ++ if (err) ++ return err; ++ ++ return mt798x_phy_hw_led_on_set(phydev, index, (value != LED_OFF)); ++} ++ ++static const unsigned long supported_triggers = ++ BIT(TRIGGER_NETDEV_FULL_DUPLEX) | ++ BIT(TRIGGER_NETDEV_HALF_DUPLEX) | ++ BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX); ++ ++static int mt798x_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ if (index > 1) ++ return -EINVAL; ++ ++ /* All combinations of the supported triggers are allowed */ ++ if (rules & ~supported_triggers) ++ return -EOPNOTSUPP; ++ ++ return 0; ++}; ++ ++static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); ++ unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); ++ unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ int on, blink; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ on = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL); ++ ++ if (on < 0) ++ return -EIO; ++ ++ blink = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ index ? MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL); ++ if (blink < 0) ++ return -EIO; ++ ++ if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | ++ MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) || ++ (blink & (MTK_PHY_LED_BLINK_RX | MTK_PHY_LED_BLINK_TX))) ++ set_bit(bit_netdev, &priv->led_state); ++ else ++ clear_bit(bit_netdev, &priv->led_state); ++ ++ if (on & MTK_PHY_LED_ON_FORCE_ON) ++ set_bit(bit_on, &priv->led_state); ++ else ++ clear_bit(bit_on, &priv->led_state); ++ ++ if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK) ++ set_bit(bit_blink, &priv->led_state); ++ else ++ clear_bit(bit_blink, &priv->led_state); ++ ++ if (!rules) ++ return 0; ++ ++ if (on & MTK_PHY_LED_ON_LINK) ++ *rules |= BIT(TRIGGER_NETDEV_LINK); ++ ++ if (on & MTK_PHY_LED_ON_LINK10) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_10); ++ ++ if (on & MTK_PHY_LED_ON_LINK100) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_100); ++ ++ if (on & MTK_PHY_LED_ON_LINK1000) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_1000); ++ ++ if (on & MTK_PHY_LED_ON_FDX) ++ *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX); ++ ++ if (on & MTK_PHY_LED_ON_HDX) ++ *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX); ++ ++ if (blink & MTK_PHY_LED_BLINK_RX) ++ *rules |= BIT(TRIGGER_NETDEV_RX); ++ ++ if (blink & MTK_PHY_LED_BLINK_TX) ++ *rules |= BIT(TRIGGER_NETDEV_TX); ++ ++ return 0; ++}; ++ ++static int mt798x_phy_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ u16 on = 0, blink = 0; ++ int ret; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) ++ on |= MTK_PHY_LED_ON_FDX; ++ ++ if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) ++ on |= MTK_PHY_LED_ON_HDX; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK10; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK100; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK1000; ++ ++ if (rules & BIT(TRIGGER_NETDEV_RX)) { ++ blink |= (on & MTK_PHY_LED_ON_LINK) ? ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000RX : 0)) : ++ MTK_PHY_LED_BLINK_RX; ++ } ++ ++ if (rules & BIT(TRIGGER_NETDEV_TX)) { ++ blink |= (on & MTK_PHY_LED_ON_LINK) ? ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000TX : 0)) : ++ MTK_PHY_LED_BLINK_TX; ++ } ++ ++ if (blink || on) ++ set_bit(bit_netdev, &priv->led_state); ++ else ++ clear_bit(bit_netdev, &priv->led_state); ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : ++ MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED_ON_FDX | ++ MTK_PHY_LED_ON_HDX | ++ MTK_PHY_LED_ON_LINK, ++ on); ++ ++ if (ret) ++ return ret; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL, blink); ++}; ++ ++static bool mt7988_phy_led_get_polarity(struct phy_device *phydev, int led_num) ++{ ++ struct mtk_socphy_shared *priv = phydev->shared->priv; ++ u32 polarities; ++ ++ if (led_num == 0) ++ polarities = ~(priv->boottrap); ++ else ++ polarities = MTK_PHY_LED1_DEFAULT_POLARITIES; ++ ++ if (polarities & BIT(phydev->mdio.addr)) ++ return true; ++ ++ return false; ++} ++ ++static int mt7988_phy_fix_leds_polarities(struct phy_device *phydev) ++{ ++ struct pinctrl *pinctrl; ++ int index; ++ ++ /* Setup LED polarity according to bootstrap use of LED pins */ ++ for (index = 0; index < 2; ++index) ++ phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED_ON_POLARITY, ++ mt7988_phy_led_get_polarity(phydev, index) ? ++ MTK_PHY_LED_ON_POLARITY : 0); ++ ++ /* Only now setup pinctrl to avoid bogus blinking */ ++ pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led"); ++ if (IS_ERR(pinctrl)) ++ dev_err(&phydev->mdio.bus->dev, ++ "Failed to setup PHY LED pinctrl\n"); ++ ++ return 0; ++} ++ ++static int mt7988_phy_probe_shared(struct phy_device *phydev) ++{ ++ struct device_node *np = dev_of_node(&phydev->mdio.bus->dev); ++ struct mtk_socphy_shared *shared = phydev->shared->priv; ++ struct regmap *regmap; ++ u32 reg; ++ int ret; ++ ++ /* The LED0 of the 4 PHYs in MT7988 are wired to SoC pins LED_A, LED_B, ++ * LED_C and LED_D respectively. At the same time those pins are used to ++ * bootstrap configuration of the reference clock source (LED_A), ++ * DRAM DDRx16b x2/x1 (LED_B) and boot device (LED_C, LED_D). ++ * In practice this is done using a LED and a resistor pulling the pin ++ * either to GND or to VIO. ++ * The detected value at boot time is accessible at run-time using the ++ * TPBANK0 register located in the gpio base of the pinctrl, in order ++ * to read it here it needs to be referenced by a phandle called ++ * 'mediatek,pio' in the MDIO bus hosting the PHY. ++ * The 4 bits in TPBANK0 are kept as package shared data and are used to ++ * set LED polarity for each of the LED0. ++ */ ++ regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,pio"); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ ret = regmap_read(regmap, RG_GPIO_MISC_TPBANK0, ®); ++ if (ret) ++ return ret; ++ ++ shared->boottrap = FIELD_GET(RG_GPIO_MISC_TPBANK0_BOOTMODE, reg); ++ ++ return 0; ++} ++ ++static void mt798x_phy_leds_state_init(struct phy_device *phydev) ++{ ++ int i; ++ ++ for (i = 0; i < 2; ++i) ++ mt798x_phy_led_hw_control_get(phydev, i, NULL); ++} ++ ++static int mt7988_phy_probe(struct phy_device *phydev) ++{ ++ struct mtk_socphy_shared *shared; ++ struct mtk_socphy_priv *priv; ++ int err; ++ ++ if (phydev->mdio.addr > 3) ++ return -EINVAL; ++ ++ err = devm_phy_package_join(&phydev->mdio.dev, phydev, 0, ++ sizeof(struct mtk_socphy_shared)); ++ if (err) ++ return err; ++ ++ if (phy_package_probe_once(phydev)) { ++ err = mt7988_phy_probe_shared(phydev); ++ if (err) ++ return err; ++ } ++ ++ shared = phydev->shared->priv; ++ priv = &shared->priv[phydev->mdio.addr]; ++ ++ phydev->priv = priv; ++ ++ mt798x_phy_leds_state_init(phydev); ++ ++ err = mt7988_phy_fix_leds_polarities(phydev); ++ if (err) ++ return err; ++ ++ /* Disable TX power saving at probing to: ++ * 1. Meet common mode compliance test criteria ++ * 2. Make sure that TX-VCM calibration works fine ++ */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, ++ MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); ++ ++ return mt798x_phy_calibration(phydev); ++} ++ ++static int mt7981_phy_probe(struct phy_device *phydev) ++{ ++ struct mtk_socphy_priv *priv; ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct mtk_socphy_priv), ++ GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ phydev->priv = priv; ++ ++ mt798x_phy_leds_state_init(phydev); ++ ++ return mt798x_phy_calibration(phydev); ++} ++ ++static struct phy_driver mtk_socphy_driver[] = { ++ { ++ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981), ++ .name = "MediaTek MT7981 PHY", ++ .config_init = mt798x_phy_config_init, ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .probe = mt7981_phy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_socphy_read_page, ++ .write_page = mtk_socphy_write_page, ++ .led_blink_set = mt798x_phy_led_blink_set, ++ .led_brightness_set = mt798x_phy_led_brightness_set, ++ .led_hw_is_supported = mt798x_phy_led_hw_is_supported, ++ .led_hw_control_set = mt798x_phy_led_hw_control_set, ++ .led_hw_control_get = mt798x_phy_led_hw_control_get, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988), ++ .name = "MediaTek MT7988 PHY", ++ .config_init = mt798x_phy_config_init, ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .probe = mt7988_phy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_socphy_read_page, ++ .write_page = mtk_socphy_write_page, ++ .led_blink_set = mt798x_phy_led_blink_set, ++ .led_brightness_set = mt798x_phy_led_brightness_set, ++ .led_hw_is_supported = mt798x_phy_led_hw_is_supported, ++ .led_hw_control_set = mt798x_phy_led_hw_control_set, ++ .led_hw_control_get = mt798x_phy_led_hw_control_get, ++ }, ++}; ++ ++module_phy_driver(mtk_socphy_driver); ++ ++static struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = { ++ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981) }, ++ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988) }, ++ { } ++}; ++ ++MODULE_DESCRIPTION("MediaTek SoC Gigabit Ethernet PHY driver"); ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_AUTHOR("SkyLake Huang "); ++MODULE_LICENSE("GPL"); ++ ++MODULE_DEVICE_TABLE(mdio, mtk_socphy_tbl); +--- /dev/null ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -0,0 +1,111 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++#include ++#include ++#include ++ ++#define MTK_EXT_PAGE_ACCESS 0x1f ++#define MTK_PHY_PAGE_STANDARD 0x0000 ++#define MTK_PHY_PAGE_EXTENDED 0x0001 ++#define MTK_PHY_PAGE_EXTENDED_2 0x0002 ++#define MTK_PHY_PAGE_EXTENDED_3 0x0003 ++#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 ++#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 ++ ++static int mtk_gephy_read_page(struct phy_device *phydev) ++{ ++ return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); ++} ++ ++static int mtk_gephy_write_page(struct phy_device *phydev, int page) ++{ ++ return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); ++} ++ ++static void mtk_gephy_config_init(struct phy_device *phydev) ++{ ++ /* Enable HW auto downshift */ ++ phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4)); ++ ++ /* Increase SlvDPSready time */ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ __phy_write(phydev, 0x10, 0xafae); ++ __phy_write(phydev, 0x12, 0x2f); ++ __phy_write(phydev, 0x10, 0x8fae); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ /* Adjust 100_mse_threshold */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff); ++ ++ /* Disable mcc */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300); ++} ++ ++static int mt7530_phy_config_init(struct phy_device *phydev) ++{ ++ mtk_gephy_config_init(phydev); ++ ++ /* Increase post_update_timer */ ++ phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b); ++ ++ return 0; ++} ++ ++static int mt7531_phy_config_init(struct phy_device *phydev) ++{ ++ mtk_gephy_config_init(phydev); ++ ++ /* PHY link down power saving enable */ ++ phy_set_bits(phydev, 0x17, BIT(4)); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300); ++ ++ /* Set TX Pair delay selection */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); ++ ++ return 0; ++} ++ ++static struct phy_driver mtk_gephy_driver[] = { ++ { ++ PHY_ID_MATCH_EXACT(0x03a29412), ++ .name = "MediaTek MT7530 PHY", ++ .config_init = mt7530_phy_config_init, ++ /* Interrupts are handled by the switch, not the PHY ++ * itself. ++ */ ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_gephy_read_page, ++ .write_page = mtk_gephy_write_page, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(0x03a29441), ++ .name = "MediaTek MT7531 PHY", ++ .config_init = mt7531_phy_config_init, ++ /* Interrupts are handled by the switch, not the PHY ++ * itself. ++ */ ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_gephy_read_page, ++ .write_page = mtk_gephy_write_page, ++ }, ++}; ++ ++module_phy_driver(mtk_gephy_driver); ++ ++static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { ++ { PHY_ID_MATCH_EXACT(0x03a29441) }, ++ { PHY_ID_MATCH_EXACT(0x03a29412) }, ++ { } ++}; ++ ++MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver"); ++MODULE_AUTHOR("DENG, Qingfang "); ++MODULE_LICENSE("GPL"); ++ ++MODULE_DEVICE_TABLE(mdio, mtk_gephy_tbl); diff --git a/target/linux/generic/backport-6.12/720-05-v6.13-net-phy-mediatek-Move-LED-helper-functions-into-mtk-.patch b/target/linux/generic/backport-6.12/720-05-v6.13-net-phy-mediatek-Move-LED-helper-functions-into-mtk-.patch new file mode 100644 index 00000000000..63407ac55c8 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-05-v6.13-net-phy-mediatek-Move-LED-helper-functions-into-mtk-.patch @@ -0,0 +1,774 @@ +From 71d88c7409b91c853d7f9c933f5e27933d656e5e Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Sat, 9 Nov 2024 00:34:52 +0800 +Subject: [PATCH 05/20] net: phy: mediatek: Move LED helper functions into mtk + phy lib + +This patch creates mtk-phy-lib.c & mtk-phy.h and integrates mtk-ge-soc.c's +LED helper functions so that we can use those helper functions in other +MTK's ethernet phy driver. + +Reviewed-by: Andrew Lunn +Signed-off-by: SkyLake.Huang +Signed-off-by: David S. Miller +--- + MAINTAINERS | 2 + + drivers/net/phy/mediatek/Kconfig | 4 + + drivers/net/phy/mediatek/Makefile | 1 + + drivers/net/phy/mediatek/mtk-ge-soc.c | 280 +++---------------------- + drivers/net/phy/mediatek/mtk-phy-lib.c | 254 ++++++++++++++++++++++ + drivers/net/phy/mediatek/mtk.h | 86 ++++++++ + 6 files changed, 372 insertions(+), 255 deletions(-) + create mode 100644 drivers/net/phy/mediatek/mtk-phy-lib.c + create mode 100644 drivers/net/phy/mediatek/mtk.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -14428,7 +14428,9 @@ M: SkyLake Huang + #include + ++#include "mtk.h" ++ + #define MTK_GPHY_ID_MT7981 0x03a29461 + #define MTK_GPHY_ID_MT7988 0x03a29481 + +@@ -210,41 +212,6 @@ + #define MTK_PHY_DA_TX_R50_PAIR_D 0x540 + + /* Registers on MDIO_MMD_VEND2 */ +-#define MTK_PHY_LED0_ON_CTRL 0x24 +-#define MTK_PHY_LED1_ON_CTRL 0x26 +-#define MTK_PHY_LED_ON_MASK GENMASK(6, 0) +-#define MTK_PHY_LED_ON_LINK1000 BIT(0) +-#define MTK_PHY_LED_ON_LINK100 BIT(1) +-#define MTK_PHY_LED_ON_LINK10 BIT(2) +-#define MTK_PHY_LED_ON_LINK (MTK_PHY_LED_ON_LINK10 |\ +- MTK_PHY_LED_ON_LINK100 |\ +- MTK_PHY_LED_ON_LINK1000) +-#define MTK_PHY_LED_ON_LINKDOWN BIT(3) +-#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ +-#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ +-#define MTK_PHY_LED_ON_FORCE_ON BIT(6) +-#define MTK_PHY_LED_ON_POLARITY BIT(14) +-#define MTK_PHY_LED_ON_ENABLE BIT(15) +- +-#define MTK_PHY_LED0_BLINK_CTRL 0x25 +-#define MTK_PHY_LED1_BLINK_CTRL 0x27 +-#define MTK_PHY_LED_BLINK_1000TX BIT(0) +-#define MTK_PHY_LED_BLINK_1000RX BIT(1) +-#define MTK_PHY_LED_BLINK_100TX BIT(2) +-#define MTK_PHY_LED_BLINK_100RX BIT(3) +-#define MTK_PHY_LED_BLINK_10TX BIT(4) +-#define MTK_PHY_LED_BLINK_10RX BIT(5) +-#define MTK_PHY_LED_BLINK_RX (MTK_PHY_LED_BLINK_10RX |\ +- MTK_PHY_LED_BLINK_100RX |\ +- MTK_PHY_LED_BLINK_1000RX) +-#define MTK_PHY_LED_BLINK_TX (MTK_PHY_LED_BLINK_10TX |\ +- MTK_PHY_LED_BLINK_100TX |\ +- MTK_PHY_LED_BLINK_1000TX) +-#define MTK_PHY_LED_BLINK_COLLISION BIT(6) +-#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) +-#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) +-#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9) +- + #define MTK_PHY_LED1_DEFAULT_POLARITIES BIT(1) + + #define MTK_PHY_RG_BG_RASEL 0x115 +@@ -299,14 +266,6 @@ enum CAL_MODE { + SW_M + }; + +-#define MTK_PHY_LED_STATE_FORCE_ON 0 +-#define MTK_PHY_LED_STATE_FORCE_BLINK 1 +-#define MTK_PHY_LED_STATE_NETDEV 2 +- +-struct mtk_socphy_priv { +- unsigned long led_state; +-}; +- + struct mtk_socphy_shared { + u32 boottrap; + struct mtk_socphy_priv priv[4]; +@@ -1172,76 +1131,23 @@ static int mt798x_phy_config_init(struct + return mt798x_phy_calibration(phydev); + } + +-static int mt798x_phy_hw_led_on_set(struct phy_device *phydev, u8 index, +- bool on) +-{ +- unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- bool changed; +- +- if (on) +- changed = !test_and_set_bit(bit_on, &priv->led_state); +- else +- changed = !!test_and_clear_bit(bit_on, &priv->led_state); +- +- changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV + +- (index ? 16 : 0), &priv->led_state); +- if (changed) +- return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : +- MTK_PHY_LED0_ON_CTRL, +- MTK_PHY_LED_ON_MASK, +- on ? MTK_PHY_LED_ON_FORCE_ON : 0); +- else +- return 0; +-} +- +-static int mt798x_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, +- bool blinking) +-{ +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + +- (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- bool changed; +- +- if (blinking) +- changed = !test_and_set_bit(bit_blink, &priv->led_state); +- else +- changed = !!test_and_clear_bit(bit_blink, &priv->led_state); +- +- changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV + +- (index ? 16 : 0), &priv->led_state); +- if (changed) +- return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL, +- blinking ? +- MTK_PHY_LED_BLINK_FORCE_BLINK : 0); +- else +- return 0; +-} +- + static int mt798x_phy_led_blink_set(struct phy_device *phydev, u8 index, + unsigned long *delay_on, + unsigned long *delay_off) + { + bool blinking = false; +- int err = 0; +- +- if (index > 1) +- return -EINVAL; ++ int err; + +- if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { +- blinking = true; +- *delay_on = 50; +- *delay_off = 50; +- } ++ err = mtk_phy_led_num_dly_cfg(index, delay_on, delay_off, &blinking); ++ if (err < 0) ++ return err; + +- err = mt798x_phy_hw_led_blink_set(phydev, index, blinking); ++ err = mtk_phy_hw_led_blink_set(phydev, index, blinking); + if (err) + return err; + +- return mt798x_phy_hw_led_on_set(phydev, index, false); ++ return mtk_phy_hw_led_on_set(phydev, index, MTK_GPHY_LED_ON_MASK, ++ false); + } + + static int mt798x_phy_led_brightness_set(struct phy_device *phydev, +@@ -1249,11 +1155,12 @@ static int mt798x_phy_led_brightness_set + { + int err; + +- err = mt798x_phy_hw_led_blink_set(phydev, index, false); ++ err = mtk_phy_hw_led_blink_set(phydev, index, false); + if (err) + return err; + +- return mt798x_phy_hw_led_on_set(phydev, index, (value != LED_OFF)); ++ return mtk_phy_hw_led_on_set(phydev, index, MTK_GPHY_LED_ON_MASK, ++ (value != LED_OFF)); + } + + static const unsigned long supported_triggers = +@@ -1269,155 +1176,26 @@ static const unsigned long supported_tri + static int mt798x_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) + { +- if (index > 1) +- return -EINVAL; +- +- /* All combinations of the supported triggers are allowed */ +- if (rules & ~supported_triggers) +- return -EOPNOTSUPP; +- +- return 0; +-}; ++ return mtk_phy_led_hw_is_supported(phydev, index, rules, ++ supported_triggers); ++} + + static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) + { +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + +- (index ? 16 : 0); +- unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); +- unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- int on, blink; +- +- if (index > 1) +- return -EINVAL; +- +- on = phy_read_mmd(phydev, MDIO_MMD_VEND2, +- index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL); +- +- if (on < 0) +- return -EIO; +- +- blink = phy_read_mmd(phydev, MDIO_MMD_VEND2, +- index ? MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL); +- if (blink < 0) +- return -EIO; +- +- if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | +- MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) || +- (blink & (MTK_PHY_LED_BLINK_RX | MTK_PHY_LED_BLINK_TX))) +- set_bit(bit_netdev, &priv->led_state); +- else +- clear_bit(bit_netdev, &priv->led_state); +- +- if (on & MTK_PHY_LED_ON_FORCE_ON) +- set_bit(bit_on, &priv->led_state); +- else +- clear_bit(bit_on, &priv->led_state); +- +- if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK) +- set_bit(bit_blink, &priv->led_state); +- else +- clear_bit(bit_blink, &priv->led_state); +- +- if (!rules) +- return 0; +- +- if (on & MTK_PHY_LED_ON_LINK) +- *rules |= BIT(TRIGGER_NETDEV_LINK); +- +- if (on & MTK_PHY_LED_ON_LINK10) +- *rules |= BIT(TRIGGER_NETDEV_LINK_10); +- +- if (on & MTK_PHY_LED_ON_LINK100) +- *rules |= BIT(TRIGGER_NETDEV_LINK_100); +- +- if (on & MTK_PHY_LED_ON_LINK1000) +- *rules |= BIT(TRIGGER_NETDEV_LINK_1000); +- +- if (on & MTK_PHY_LED_ON_FDX) +- *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX); +- +- if (on & MTK_PHY_LED_ON_HDX) +- *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX); +- +- if (blink & MTK_PHY_LED_BLINK_RX) +- *rules |= BIT(TRIGGER_NETDEV_RX); +- +- if (blink & MTK_PHY_LED_BLINK_TX) +- *rules |= BIT(TRIGGER_NETDEV_TX); +- +- return 0; ++ return mtk_phy_led_hw_ctrl_get(phydev, index, rules, ++ MTK_GPHY_LED_ON_SET, ++ MTK_GPHY_LED_RX_BLINK_SET, ++ MTK_GPHY_LED_TX_BLINK_SET); + }; + + static int mt798x_phy_led_hw_control_set(struct phy_device *phydev, u8 index, + unsigned long rules) + { +- unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- u16 on = 0, blink = 0; +- int ret; +- +- if (index > 1) +- return -EINVAL; +- +- if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) +- on |= MTK_PHY_LED_ON_FDX; +- +- if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) +- on |= MTK_PHY_LED_ON_HDX; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK10; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK100; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK1000; +- +- if (rules & BIT(TRIGGER_NETDEV_RX)) { +- blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? +- MTK_PHY_LED_BLINK_10RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? +- MTK_PHY_LED_BLINK_100RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? +- MTK_PHY_LED_BLINK_1000RX : 0)) : +- MTK_PHY_LED_BLINK_RX; +- } +- +- if (rules & BIT(TRIGGER_NETDEV_TX)) { +- blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? +- MTK_PHY_LED_BLINK_10TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? +- MTK_PHY_LED_BLINK_100TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? +- MTK_PHY_LED_BLINK_1000TX : 0)) : +- MTK_PHY_LED_BLINK_TX; +- } +- +- if (blink || on) +- set_bit(bit_netdev, &priv->led_state); +- else +- clear_bit(bit_netdev, &priv->led_state); +- +- ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : +- MTK_PHY_LED0_ON_CTRL, +- MTK_PHY_LED_ON_FDX | +- MTK_PHY_LED_ON_HDX | +- MTK_PHY_LED_ON_LINK, +- on); +- +- if (ret) +- return ret; +- +- return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL, blink); ++ return mtk_phy_led_hw_ctrl_set(phydev, index, rules, ++ MTK_GPHY_LED_ON_SET, ++ MTK_GPHY_LED_RX_BLINK_SET, ++ MTK_GPHY_LED_TX_BLINK_SET); + }; + + static bool mt7988_phy_led_get_polarity(struct phy_device *phydev, int led_num) +@@ -1492,14 +1270,6 @@ static int mt7988_phy_probe_shared(struc + return 0; + } + +-static void mt798x_phy_leds_state_init(struct phy_device *phydev) +-{ +- int i; +- +- for (i = 0; i < 2; ++i) +- mt798x_phy_led_hw_control_get(phydev, i, NULL); +-} +- + static int mt7988_phy_probe(struct phy_device *phydev) + { + struct mtk_socphy_shared *shared; +@@ -1525,7 +1295,7 @@ static int mt7988_phy_probe(struct phy_d + + phydev->priv = priv; + +- mt798x_phy_leds_state_init(phydev); ++ mtk_phy_leds_state_init(phydev); + + err = mt7988_phy_fix_leds_polarities(phydev); + if (err) +@@ -1552,7 +1322,7 @@ static int mt7981_phy_probe(struct phy_d + + phydev->priv = priv; + +- mt798x_phy_leds_state_init(phydev); ++ mtk_phy_leds_state_init(phydev); + + return mt798x_phy_calibration(phydev); + } +--- /dev/null ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -0,0 +1,254 @@ ++// SPDX-License-Identifier: GPL-2.0 ++#include ++#include ++ ++#include ++ ++#include "mtk.h" ++ ++int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules, ++ unsigned long supported_triggers) ++{ ++ if (index > 1) ++ return -EINVAL; ++ ++ /* All combinations of the supported triggers are allowed */ ++ if (rules & ~supported_triggers) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_phy_led_hw_is_supported); ++ ++int mtk_phy_led_hw_ctrl_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules, u16 on_set, ++ u16 rx_blink_set, u16 tx_blink_set) ++{ ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); ++ unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); ++ unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ int on, blink; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ on = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL); ++ ++ if (on < 0) ++ return -EIO; ++ ++ blink = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ index ? MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL); ++ if (blink < 0) ++ return -EIO; ++ ++ if ((on & (on_set | MTK_PHY_LED_ON_FDX | ++ MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) || ++ (blink & (rx_blink_set | tx_blink_set))) ++ set_bit(bit_netdev, &priv->led_state); ++ else ++ clear_bit(bit_netdev, &priv->led_state); ++ ++ if (on & MTK_PHY_LED_ON_FORCE_ON) ++ set_bit(bit_on, &priv->led_state); ++ else ++ clear_bit(bit_on, &priv->led_state); ++ ++ if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK) ++ set_bit(bit_blink, &priv->led_state); ++ else ++ clear_bit(bit_blink, &priv->led_state); ++ ++ if (!rules) ++ return 0; ++ ++ if (on & on_set) ++ *rules |= BIT(TRIGGER_NETDEV_LINK); ++ ++ if (on & MTK_PHY_LED_ON_LINK10) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_10); ++ ++ if (on & MTK_PHY_LED_ON_LINK100) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_100); ++ ++ if (on & MTK_PHY_LED_ON_LINK1000) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_1000); ++ ++ if (on & MTK_PHY_LED_ON_LINK2500) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_2500); ++ ++ if (on & MTK_PHY_LED_ON_FDX) ++ *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX); ++ ++ if (on & MTK_PHY_LED_ON_HDX) ++ *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX); ++ ++ if (blink & rx_blink_set) ++ *rules |= BIT(TRIGGER_NETDEV_RX); ++ ++ if (blink & tx_blink_set) ++ *rules |= BIT(TRIGGER_NETDEV_TX); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_phy_led_hw_ctrl_get); ++ ++int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index, ++ unsigned long rules, u16 on_set, ++ u16 rx_blink_set, u16 tx_blink_set) ++{ ++ unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ u16 on = 0, blink = 0; ++ int ret; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) ++ on |= MTK_PHY_LED_ON_FDX; ++ ++ if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) ++ on |= MTK_PHY_LED_ON_HDX; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK10; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK100; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK1000; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK2500; ++ ++ if (rules & BIT(TRIGGER_NETDEV_RX)) { ++ blink |= (on & on_set) ? ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK2500) ? ++ MTK_PHY_LED_BLINK_2500RX : 0)) : ++ rx_blink_set; ++ } ++ ++ if (rules & BIT(TRIGGER_NETDEV_TX)) { ++ blink |= (on & on_set) ? ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK2500) ? ++ MTK_PHY_LED_BLINK_2500TX : 0)) : ++ tx_blink_set; ++ } ++ ++ if (blink || on) ++ set_bit(bit_netdev, &priv->led_state); ++ else ++ clear_bit(bit_netdev, &priv->led_state); ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX | on_set, ++ on); ++ ++ if (ret) ++ return ret; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL, blink); ++} ++EXPORT_SYMBOL_GPL(mtk_phy_led_hw_ctrl_set); ++ ++int mtk_phy_led_num_dly_cfg(u8 index, unsigned long *delay_on, ++ unsigned long *delay_off, bool *blinking) ++{ ++ if (index > 1) ++ return -EINVAL; ++ ++ if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { ++ *blinking = true; ++ *delay_on = 50; ++ *delay_off = 50; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_phy_led_num_dly_cfg); ++ ++int mtk_phy_hw_led_on_set(struct phy_device *phydev, u8 index, ++ u16 led_on_mask, bool on) ++{ ++ unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (on) ++ changed = !test_and_set_bit(bit_on, &priv->led_state); ++ else ++ changed = !!test_and_clear_bit(bit_on, &priv->led_state); ++ ++ changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV + ++ (index ? 16 : 0), &priv->led_state); ++ if (changed) ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : ++ MTK_PHY_LED0_ON_CTRL, ++ led_on_mask, ++ on ? MTK_PHY_LED_ON_FORCE_ON : 0); ++ else ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_phy_hw_led_on_set); ++ ++int mtk_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, bool blinking) ++{ ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (blinking) ++ changed = !test_and_set_bit(bit_blink, &priv->led_state); ++ else ++ changed = !!test_and_clear_bit(bit_blink, &priv->led_state); ++ ++ changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV + ++ (index ? 16 : 0), &priv->led_state); ++ if (changed) ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL, ++ blinking ? ++ MTK_PHY_LED_BLINK_FORCE_BLINK : 0); ++ else ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_phy_hw_led_blink_set); ++ ++void mtk_phy_leds_state_init(struct phy_device *phydev) ++{ ++ int i; ++ ++ for (i = 0; i < 2; ++i) ++ phydev->drv->led_hw_control_get(phydev, i, NULL); ++} ++EXPORT_SYMBOL_GPL(mtk_phy_leds_state_init); ++ ++MODULE_DESCRIPTION("MediaTek Ethernet PHY driver common"); ++MODULE_AUTHOR("Sky Huang "); ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -0,0 +1,86 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Common definition for Mediatek Ethernet PHYs ++ * Author: SkyLake Huang ++ * Copyright (c) 2024 MediaTek Inc. ++ */ ++ ++#ifndef _MTK_EPHY_H_ ++#define _MTK_EPHY_H_ ++ ++#define MTK_EXT_PAGE_ACCESS 0x1f ++ ++/* Registers on MDIO_MMD_VEND2 */ ++#define MTK_PHY_LED0_ON_CTRL 0x24 ++#define MTK_PHY_LED1_ON_CTRL 0x26 ++#define MTK_GPHY_LED_ON_MASK GENMASK(6, 0) ++#define MTK_2P5GPHY_LED_ON_MASK GENMASK(7, 0) ++#define MTK_PHY_LED_ON_LINK1000 BIT(0) ++#define MTK_PHY_LED_ON_LINK100 BIT(1) ++#define MTK_PHY_LED_ON_LINK10 BIT(2) ++#define MTK_PHY_LED_ON_LINKDOWN BIT(3) ++#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ ++#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ ++#define MTK_PHY_LED_ON_FORCE_ON BIT(6) ++#define MTK_PHY_LED_ON_LINK2500 BIT(7) ++#define MTK_PHY_LED_ON_POLARITY BIT(14) ++#define MTK_PHY_LED_ON_ENABLE BIT(15) ++ ++#define MTK_PHY_LED0_BLINK_CTRL 0x25 ++#define MTK_PHY_LED1_BLINK_CTRL 0x27 ++#define MTK_PHY_LED_BLINK_1000TX BIT(0) ++#define MTK_PHY_LED_BLINK_1000RX BIT(1) ++#define MTK_PHY_LED_BLINK_100TX BIT(2) ++#define MTK_PHY_LED_BLINK_100RX BIT(3) ++#define MTK_PHY_LED_BLINK_10TX BIT(4) ++#define MTK_PHY_LED_BLINK_10RX BIT(5) ++#define MTK_PHY_LED_BLINK_COLLISION BIT(6) ++#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) ++#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) ++#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9) ++#define MTK_PHY_LED_BLINK_2500TX BIT(10) ++#define MTK_PHY_LED_BLINK_2500RX BIT(11) ++ ++#define MTK_GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK1000 | \ ++ MTK_PHY_LED_ON_LINK100 | \ ++ MTK_PHY_LED_ON_LINK10) ++#define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ ++ MTK_PHY_LED_BLINK_100RX | \ ++ MTK_PHY_LED_BLINK_10RX) ++#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ ++ MTK_PHY_LED_BLINK_100RX | \ ++ MTK_PHY_LED_BLINK_10RX) ++ ++#define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \ ++ MTK_GPHY_LED_ON_SET) ++#define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ ++ MTK_GPHY_LED_RX_BLINK_SET) ++#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ ++ MTK_GPHY_LED_TX_BLINK_SET) ++ ++#define MTK_PHY_LED_STATE_FORCE_ON 0 ++#define MTK_PHY_LED_STATE_FORCE_BLINK 1 ++#define MTK_PHY_LED_STATE_NETDEV 2 ++ ++struct mtk_socphy_priv { ++ unsigned long led_state; ++}; ++ ++int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules, ++ unsigned long supported_triggers); ++int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index, ++ unsigned long rules, u16 on_set, ++ u16 rx_blink_set, u16 tx_blink_set); ++int mtk_phy_led_hw_ctrl_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules, u16 on_set, ++ u16 rx_blink_set, u16 tx_blink_set); ++int mtk_phy_led_num_dly_cfg(u8 index, unsigned long *delay_on, ++ unsigned long *delay_off, bool *blinking); ++int mtk_phy_hw_led_on_set(struct phy_device *phydev, u8 index, ++ u16 led_on_mask, bool on); ++int mtk_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, ++ bool blinking); ++void mtk_phy_leds_state_init(struct phy_device *phydev); ++ ++#endif /* _MTK_EPHY_H_ */ diff --git a/target/linux/generic/backport-6.12/720-06-v6.13-net-phy-mediatek-Improve-readability-of-mtk-phy-lib..patch b/target/linux/generic/backport-6.12/720-06-v6.13-net-phy-mediatek-Improve-readability-of-mtk-phy-lib..patch new file mode 100644 index 00000000000..19f3a84ad93 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-06-v6.13-net-phy-mediatek-Improve-readability-of-mtk-phy-lib..patch @@ -0,0 +1,72 @@ +From 3efd0595fc7aaae300f5d9f4f0ae86f432c8d2c7 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Sat, 9 Nov 2024 00:34:53 +0800 +Subject: [PATCH 06/20] net: phy: mediatek: Improve readability of + mtk-phy-lib.c's mtk_phy_led_hw_ctrl_set() + +This patch removes parens around TRIGGER_NETDEV_RX/TRIGGER_NETDEV_TX in +mtk_phy_led_hw_ctrl_set(), which improves readability. + +Reviewed-by: Andrew Lunn +Signed-off-by: SkyLake.Huang +Signed-off-by: David S. Miller +--- + drivers/net/phy/mediatek/mtk-phy-lib.c | 44 ++++++++++++++------------ + 1 file changed, 24 insertions(+), 20 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-phy-lib.c ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -129,29 +129,33 @@ int mtk_phy_led_hw_ctrl_set(struct phy_d + on |= MTK_PHY_LED_ON_LINK2500; + + if (rules & BIT(TRIGGER_NETDEV_RX)) { +- blink |= (on & on_set) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? +- MTK_PHY_LED_BLINK_10RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? +- MTK_PHY_LED_BLINK_100RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? +- MTK_PHY_LED_BLINK_1000RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK2500) ? +- MTK_PHY_LED_BLINK_2500RX : 0)) : +- rx_blink_set; ++ if (on & on_set) { ++ if (on & MTK_PHY_LED_ON_LINK10) ++ blink |= MTK_PHY_LED_BLINK_10RX; ++ if (on & MTK_PHY_LED_ON_LINK100) ++ blink |= MTK_PHY_LED_BLINK_100RX; ++ if (on & MTK_PHY_LED_ON_LINK1000) ++ blink |= MTK_PHY_LED_BLINK_1000RX; ++ if (on & MTK_PHY_LED_ON_LINK2500) ++ blink |= MTK_PHY_LED_BLINK_2500RX; ++ } else { ++ blink |= rx_blink_set; ++ } + } + + if (rules & BIT(TRIGGER_NETDEV_TX)) { +- blink |= (on & on_set) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? +- MTK_PHY_LED_BLINK_10TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? +- MTK_PHY_LED_BLINK_100TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? +- MTK_PHY_LED_BLINK_1000TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK2500) ? +- MTK_PHY_LED_BLINK_2500TX : 0)) : +- tx_blink_set; ++ if (on & on_set) { ++ if (on & MTK_PHY_LED_ON_LINK10) ++ blink |= MTK_PHY_LED_BLINK_10TX; ++ if (on & MTK_PHY_LED_ON_LINK100) ++ blink |= MTK_PHY_LED_BLINK_100TX; ++ if (on & MTK_PHY_LED_ON_LINK1000) ++ blink |= MTK_PHY_LED_BLINK_1000TX; ++ if (on & MTK_PHY_LED_ON_LINK2500) ++ blink |= MTK_PHY_LED_BLINK_2500TX; ++ } else { ++ blink |= tx_blink_set; ++ } + } + + if (blink || on) diff --git a/target/linux/generic/backport-6.12/720-07-v6.13-net-phy-mediatek-Integrate-read-write-page-helper-fu.patch b/target/linux/generic/backport-6.12/720-07-v6.13-net-phy-mediatek-Integrate-read-write-page-helper-fu.patch new file mode 100644 index 00000000000..a5828bc759f --- /dev/null +++ b/target/linux/generic/backport-6.12/720-07-v6.13-net-phy-mediatek-Integrate-read-write-page-helper-fu.patch @@ -0,0 +1,153 @@ +From 50a97d716105a5f35aaecca0bdfe8e23cba0e87f Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Sat, 9 Nov 2024 00:34:54 +0800 +Subject: [PATCH 07/20] net: phy: mediatek: Integrate read/write page helper + functions + +This patch integrates read/write page helper functions as MTK phy lib. +They are basically the same in mtk-ge.c & mtk-ge-soc.c. + +Signed-off-by: SkyLake.Huang +Signed-off-by: David S. Miller +--- + drivers/net/phy/mediatek/Kconfig | 1 + + drivers/net/phy/mediatek/mtk-ge-soc.c | 18 ++++-------------- + drivers/net/phy/mediatek/mtk-ge.c | 20 ++++++-------------- + drivers/net/phy/mediatek/mtk-phy-lib.c | 12 ++++++++++++ + drivers/net/phy/mediatek/mtk.h | 3 +++ + 5 files changed, 26 insertions(+), 28 deletions(-) + +--- a/drivers/net/phy/mediatek/Kconfig ++++ b/drivers/net/phy/mediatek/Kconfig +@@ -4,6 +4,7 @@ config MTK_NET_PHYLIB + + config MEDIATEK_GE_PHY + tristate "MediaTek Gigabit Ethernet PHYs" ++ select MTK_NET_PHYLIB + help + Supports the MediaTek non-built-in Gigabit Ethernet PHYs. + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -271,16 +271,6 @@ struct mtk_socphy_shared { + struct mtk_socphy_priv priv[4]; + }; + +-static int mtk_socphy_read_page(struct phy_device *phydev) +-{ +- return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +-} +- +-static int mtk_socphy_write_page(struct phy_device *phydev, int page) +-{ +- return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +-} +- + /* One calibration cycle consists of: + * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high + * until AD_CAL_COMP is ready to output calibration result. +@@ -1337,8 +1327,8 @@ static struct phy_driver mtk_socphy_driv + .probe = mt7981_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, +- .read_page = mtk_socphy_read_page, +- .write_page = mtk_socphy_write_page, ++ .read_page = mtk_phy_read_page, ++ .write_page = mtk_phy_write_page, + .led_blink_set = mt798x_phy_led_blink_set, + .led_brightness_set = mt798x_phy_led_brightness_set, + .led_hw_is_supported = mt798x_phy_led_hw_is_supported, +@@ -1354,8 +1344,8 @@ static struct phy_driver mtk_socphy_driv + .probe = mt7988_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, +- .read_page = mtk_socphy_read_page, +- .write_page = mtk_socphy_write_page, ++ .read_page = mtk_phy_read_page, ++ .write_page = mtk_phy_write_page, + .led_blink_set = mt798x_phy_led_blink_set, + .led_brightness_set = mt798x_phy_led_brightness_set, + .led_hw_is_supported = mt798x_phy_led_hw_is_supported, +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -3,6 +3,8 @@ + #include + #include + ++#include "mtk.h" ++ + #define MTK_EXT_PAGE_ACCESS 0x1f + #define MTK_PHY_PAGE_STANDARD 0x0000 + #define MTK_PHY_PAGE_EXTENDED 0x0001 +@@ -11,16 +13,6 @@ + #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 + #define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + +-static int mtk_gephy_read_page(struct phy_device *phydev) +-{ +- return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +-} +- +-static int mtk_gephy_write_page(struct phy_device *phydev, int page) +-{ +- return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +-} +- + static void mtk_gephy_config_init(struct phy_device *phydev) + { + /* Enable HW auto downshift */ +@@ -77,8 +69,8 @@ static struct phy_driver mtk_gephy_drive + .handle_interrupt = genphy_handle_interrupt_no_ack, + .suspend = genphy_suspend, + .resume = genphy_resume, +- .read_page = mtk_gephy_read_page, +- .write_page = mtk_gephy_write_page, ++ .read_page = mtk_phy_read_page, ++ .write_page = mtk_phy_write_page, + }, + { + PHY_ID_MATCH_EXACT(0x03a29441), +@@ -91,8 +83,8 @@ static struct phy_driver mtk_gephy_drive + .handle_interrupt = genphy_handle_interrupt_no_ack, + .suspend = genphy_suspend, + .resume = genphy_resume, +- .read_page = mtk_gephy_read_page, +- .write_page = mtk_gephy_write_page, ++ .read_page = mtk_phy_read_page, ++ .write_page = mtk_phy_write_page, + }, + }; + +--- a/drivers/net/phy/mediatek/mtk-phy-lib.c ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -6,6 +6,18 @@ + + #include "mtk.h" + ++int mtk_phy_read_page(struct phy_device *phydev) ++{ ++ return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); ++} ++EXPORT_SYMBOL_GPL(mtk_phy_read_page); ++ ++int mtk_phy_write_page(struct phy_device *phydev, int page) ++{ ++ return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); ++} ++EXPORT_SYMBOL_GPL(mtk_phy_write_page); ++ + int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules, + unsigned long supported_triggers) +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -66,6 +66,9 @@ struct mtk_socphy_priv { + unsigned long led_state; + }; + ++int mtk_phy_read_page(struct phy_device *phydev); ++int mtk_phy_write_page(struct phy_device *phydev, int page); ++ + int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules, + unsigned long supported_triggers); diff --git a/target/linux/generic/backport-6.12/720-08-v6.13-net-phy-mediatek-add-MT7530-MT7531-s-PHY-ID-macros.patch b/target/linux/generic/backport-6.12/720-08-v6.13-net-phy-mediatek-add-MT7530-MT7531-s-PHY-ID-macros.patch new file mode 100644 index 00000000000..1c8738408b2 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-08-v6.13-net-phy-mediatek-add-MT7530-MT7531-s-PHY-ID-macros.patch @@ -0,0 +1,56 @@ +From e6579df175d5b1baa605c82f8e759542262637cf Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Sat, 9 Nov 2024 00:34:55 +0800 +Subject: [PATCH 08/20] net: phy: mediatek: add MT7530 & MT7531's PHY ID macros + +This patch adds MT7530 & MT7531's PHY ID macros in mtk-ge.c so that +it follows the same rule of mtk-ge-soc.c. + +Reviewed-by: Andrew Lunn +Signed-off-by: SkyLake.Huang +Signed-off-by: David S. Miller +--- + drivers/net/phy/mediatek/mtk-ge.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -5,6 +5,9 @@ + + #include "mtk.h" + ++#define MTK_GPHY_ID_MT7530 0x03a29412 ++#define MTK_GPHY_ID_MT7531 0x03a29441 ++ + #define MTK_EXT_PAGE_ACCESS 0x1f + #define MTK_PHY_PAGE_STANDARD 0x0000 + #define MTK_PHY_PAGE_EXTENDED 0x0001 +@@ -59,7 +62,7 @@ static int mt7531_phy_config_init(struct + + static struct phy_driver mtk_gephy_driver[] = { + { +- PHY_ID_MATCH_EXACT(0x03a29412), ++ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530), + .name = "MediaTek MT7530 PHY", + .config_init = mt7530_phy_config_init, + /* Interrupts are handled by the switch, not the PHY +@@ -73,7 +76,7 @@ static struct phy_driver mtk_gephy_drive + .write_page = mtk_phy_write_page, + }, + { +- PHY_ID_MATCH_EXACT(0x03a29441), ++ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7531), + .name = "MediaTek MT7531 PHY", + .config_init = mt7531_phy_config_init, + /* Interrupts are handled by the switch, not the PHY +@@ -91,8 +94,8 @@ static struct phy_driver mtk_gephy_drive + module_phy_driver(mtk_gephy_driver); + + static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { +- { PHY_ID_MATCH_EXACT(0x03a29441) }, +- { PHY_ID_MATCH_EXACT(0x03a29412) }, ++ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530) }, ++ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7531) }, + { } + }; + diff --git a/target/linux/generic/backport-6.12/720-09-v6.14-net-phy-Constify-struct-mdio_device_id.patch b/target/linux/generic/backport-6.12/720-09-v6.14-net-phy-Constify-struct-mdio_device_id.patch new file mode 100644 index 00000000000..c0c840d3af2 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-09-v6.14-net-phy-Constify-struct-mdio_device_id.patch @@ -0,0 +1,700 @@ +From e127f7380aaf2cd1614961d826a4af7ab297d37f Mon Sep 17 00:00:00 2001 +From: Christophe JAILLET +Date: Sun, 12 Jan 2025 15:14:50 +0100 +Subject: [PATCH 09/20] net: phy: Constify struct mdio_device_id + +'struct mdio_device_id' is not modified in these drivers. + +Constifying these structures moves some data to a read-only section, so +increase overall security. + +On a x86_64, with allmodconfig, as an example: +Before: +====== + text data bss dec hex filename + 27014 12792 0 39806 9b7e drivers/net/phy/broadcom.o + +After: +===== + text data bss dec hex filename + 27206 12600 0 39806 9b7e drivers/net/phy/broadcom.o + +Signed-off-by: Christophe JAILLET +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/403c381b7d9156b67ad68ffc44b8eee70c5e86a9.1736691226.git.christophe.jaillet@wanadoo.fr +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/adin.c | 2 +- + drivers/net/phy/adin1100.c | 2 +- + drivers/net/phy/air_en8811h.c | 2 +- + drivers/net/phy/amd.c | 2 +- + drivers/net/phy/aquantia/aquantia_main.c | 2 +- + drivers/net/phy/ax88796b.c | 2 +- + drivers/net/phy/bcm-cygnus.c | 2 +- + drivers/net/phy/bcm54140.c | 2 +- + drivers/net/phy/bcm63xx.c | 2 +- + drivers/net/phy/bcm7xxx.c | 2 +- + drivers/net/phy/bcm84881.c | 2 +- + drivers/net/phy/broadcom.c | 2 +- + drivers/net/phy/cicada.c | 2 +- + drivers/net/phy/cortina.c | 2 +- + drivers/net/phy/davicom.c | 2 +- + drivers/net/phy/dp83640.c | 2 +- + drivers/net/phy/dp83822.c | 2 +- + drivers/net/phy/dp83848.c | 2 +- + drivers/net/phy/dp83867.c | 2 +- + drivers/net/phy/dp83869.c | 2 +- + drivers/net/phy/dp83tc811.c | 2 +- + drivers/net/phy/dp83td510.c | 2 +- + drivers/net/phy/dp83tg720.c | 2 +- + drivers/net/phy/et1011c.c | 2 +- + drivers/net/phy/icplus.c | 2 +- + drivers/net/phy/intel-xway.c | 2 +- + drivers/net/phy/lxt.c | 2 +- + drivers/net/phy/marvell-88q2xxx.c | 2 +- + drivers/net/phy/marvell-88x2222.c | 2 +- + drivers/net/phy/marvell.c | 2 +- + drivers/net/phy/marvell10g.c | 2 +- + drivers/net/phy/mediatek/mtk-ge-soc.c | 2 +- + drivers/net/phy/mediatek/mtk-ge.c | 2 +- + drivers/net/phy/meson-gxl.c | 2 +- + drivers/net/phy/micrel.c | 2 +- + drivers/net/phy/microchip.c | 2 +- + drivers/net/phy/microchip_t1.c | 2 +- + drivers/net/phy/microchip_t1s.c | 2 +- + drivers/net/phy/mscc/mscc_main.c | 2 +- + drivers/net/phy/mxl-gpy.c | 2 +- + drivers/net/phy/national.c | 2 +- + drivers/net/phy/ncn26000.c | 2 +- + drivers/net/phy/nxp-c45-tja11xx.c | 2 +- + drivers/net/phy/nxp-cbtx.c | 2 +- + drivers/net/phy/nxp-tja11xx.c | 2 +- + drivers/net/phy/qcom/at803x.c | 2 +- + drivers/net/phy/qcom/qca807x.c | 2 +- + drivers/net/phy/qcom/qca808x.c | 2 +- + drivers/net/phy/qcom/qca83xx.c | 2 +- + drivers/net/phy/qsemi.c | 2 +- + drivers/net/phy/rockchip.c | 2 +- + drivers/net/phy/smsc.c | 2 +- + drivers/net/phy/ste10Xp.c | 2 +- + drivers/net/phy/teranetics.c | 2 +- + drivers/net/phy/uPD60620.c | 2 +- + drivers/net/phy/vitesse.c | 2 +- + 56 files changed, 56 insertions(+), 56 deletions(-) + +--- a/drivers/net/phy/adin.c ++++ b/drivers/net/phy/adin.c +@@ -1040,7 +1040,7 @@ static struct phy_driver adin_driver[] = + + module_phy_driver(adin_driver); + +-static struct mdio_device_id __maybe_unused adin_tbl[] = { ++static const struct mdio_device_id __maybe_unused adin_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_ADIN1200) }, + { PHY_ID_MATCH_MODEL(PHY_ID_ADIN1300) }, + { } +--- a/drivers/net/phy/adin1100.c ++++ b/drivers/net/phy/adin1100.c +@@ -340,7 +340,7 @@ static struct phy_driver adin_driver[] = + + module_phy_driver(adin_driver); + +-static struct mdio_device_id __maybe_unused adin_tbl[] = { ++static const struct mdio_device_id __maybe_unused adin_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_ADIN1100) }, + { PHY_ID_MATCH_MODEL(PHY_ID_ADIN1110) }, + { PHY_ID_MATCH_MODEL(PHY_ID_ADIN2111) }, +--- a/drivers/net/phy/air_en8811h.c ++++ b/drivers/net/phy/air_en8811h.c +@@ -1075,7 +1075,7 @@ static struct phy_driver en8811h_driver[ + + module_phy_driver(en8811h_driver); + +-static struct mdio_device_id __maybe_unused en8811h_tbl[] = { ++static const struct mdio_device_id __maybe_unused en8811h_tbl[] = { + { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) }, + { } + }; +--- a/drivers/net/phy/amd.c ++++ b/drivers/net/phy/amd.c +@@ -111,7 +111,7 @@ static struct phy_driver am79c_drivers[] + + module_phy_driver(am79c_drivers); + +-static struct mdio_device_id __maybe_unused amd_tbl[] = { ++static const struct mdio_device_id __maybe_unused amd_tbl[] = { + { PHY_ID_AC101L, 0xfffffff0 }, + { PHY_ID_AM79C874, 0xfffffff0 }, + { } +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -1096,7 +1096,7 @@ static struct phy_driver aqr_driver[] = + + module_phy_driver(aqr_driver); + +-static struct mdio_device_id __maybe_unused aqr_tbl[] = { ++static const struct mdio_device_id __maybe_unused aqr_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) }, +--- a/drivers/net/phy/ax88796b.c ++++ b/drivers/net/phy/ax88796b.c +@@ -121,7 +121,7 @@ static struct phy_driver asix_driver[] = + + module_phy_driver(asix_driver); + +-static struct mdio_device_id __maybe_unused asix_tbl[] = { ++static const struct mdio_device_id __maybe_unused asix_tbl[] = { + { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A) }, + { PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C) }, + { PHY_ID_ASIX_AX88796B, 0xfffffff0 }, +--- a/drivers/net/phy/bcm-cygnus.c ++++ b/drivers/net/phy/bcm-cygnus.c +@@ -278,7 +278,7 @@ static struct phy_driver bcm_cygnus_phy_ + } + }; + +-static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = { ++static const struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = { + { PHY_ID_BCM_CYGNUS, 0xfffffff0, }, + { PHY_ID_BCM_OMEGA, 0xfffffff0, }, + { } +--- a/drivers/net/phy/bcm54140.c ++++ b/drivers/net/phy/bcm54140.c +@@ -883,7 +883,7 @@ static struct phy_driver bcm54140_driver + }; + module_phy_driver(bcm54140_drivers); + +-static struct mdio_device_id __maybe_unused bcm54140_tbl[] = { ++static const struct mdio_device_id __maybe_unused bcm54140_tbl[] = { + { PHY_ID_BCM54140, BCM54140_PHY_ID_MASK }, + { } + }; +--- a/drivers/net/phy/bcm63xx.c ++++ b/drivers/net/phy/bcm63xx.c +@@ -93,7 +93,7 @@ static struct phy_driver bcm63xx_driver[ + + module_phy_driver(bcm63xx_driver); + +-static struct mdio_device_id __maybe_unused bcm63xx_tbl[] = { ++static const struct mdio_device_id __maybe_unused bcm63xx_tbl[] = { + { 0x00406000, 0xfffffc00 }, + { 0x002bdc00, 0xfffffc00 }, + { } +--- a/drivers/net/phy/bcm7xxx.c ++++ b/drivers/net/phy/bcm7xxx.c +@@ -929,7 +929,7 @@ static struct phy_driver bcm7xxx_driver[ + BCM7XXX_16NM_EPHY(PHY_ID_BCM7712, "Broadcom BCM7712"), + }; + +-static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { ++static const struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { + { PHY_ID_BCM72113, 0xfffffff0 }, + { PHY_ID_BCM72116, 0xfffffff0, }, + { PHY_ID_BCM72165, 0xfffffff0, }, +--- a/drivers/net/phy/bcm84881.c ++++ b/drivers/net/phy/bcm84881.c +@@ -262,7 +262,7 @@ static struct phy_driver bcm84881_driver + module_phy_driver(bcm84881_drivers); + + /* FIXME: module auto-loading for Clause 45 PHYs seems non-functional */ +-static struct mdio_device_id __maybe_unused bcm84881_tbl[] = { ++static const struct mdio_device_id __maybe_unused bcm84881_tbl[] = { + { 0xae025150, 0xfffffff0 }, + { }, + }; +--- a/drivers/net/phy/broadcom.c ++++ b/drivers/net/phy/broadcom.c +@@ -1734,7 +1734,7 @@ static struct phy_driver broadcom_driver + + module_phy_driver(broadcom_drivers); + +-static struct mdio_device_id __maybe_unused broadcom_tbl[] = { ++static const struct mdio_device_id __maybe_unused broadcom_tbl[] = { + { PHY_ID_BCM5411, 0xfffffff0 }, + { PHY_ID_BCM5421, 0xfffffff0 }, + { PHY_ID_BCM54210E, 0xfffffff0 }, +--- a/drivers/net/phy/cicada.c ++++ b/drivers/net/phy/cicada.c +@@ -145,7 +145,7 @@ static struct phy_driver cis820x_driver[ + + module_phy_driver(cis820x_driver); + +-static struct mdio_device_id __maybe_unused cicada_tbl[] = { ++static const struct mdio_device_id __maybe_unused cicada_tbl[] = { + { 0x000fc410, 0x000ffff0 }, + { 0x000fc440, 0x000fffc0 }, + { } +--- a/drivers/net/phy/cortina.c ++++ b/drivers/net/phy/cortina.c +@@ -87,7 +87,7 @@ static struct phy_driver cortina_driver[ + + module_phy_driver(cortina_driver); + +-static struct mdio_device_id __maybe_unused cortina_tbl[] = { ++static const struct mdio_device_id __maybe_unused cortina_tbl[] = { + { PHY_ID_CS4340, 0xffffffff}, + {}, + }; +--- a/drivers/net/phy/davicom.c ++++ b/drivers/net/phy/davicom.c +@@ -209,7 +209,7 @@ static struct phy_driver dm91xx_driver[] + + module_phy_driver(dm91xx_driver); + +-static struct mdio_device_id __maybe_unused davicom_tbl[] = { ++static const struct mdio_device_id __maybe_unused davicom_tbl[] = { + { 0x0181b880, 0x0ffffff0 }, + { 0x0181b8b0, 0x0ffffff0 }, + { 0x0181b8a0, 0x0ffffff0 }, +--- a/drivers/net/phy/dp83640.c ++++ b/drivers/net/phy/dp83640.c +@@ -1548,7 +1548,7 @@ MODULE_LICENSE("GPL"); + module_init(dp83640_init); + module_exit(dp83640_exit); + +-static struct mdio_device_id __maybe_unused dp83640_tbl[] = { ++static const struct mdio_device_id __maybe_unused dp83640_tbl[] = { + { DP83640_PHY_ID, 0xfffffff0 }, + { } + }; +--- a/drivers/net/phy/dp83822.c ++++ b/drivers/net/phy/dp83822.c +@@ -825,7 +825,7 @@ static struct phy_driver dp83822_driver[ + }; + module_phy_driver(dp83822_driver); + +-static struct mdio_device_id __maybe_unused dp83822_tbl[] = { ++static const struct mdio_device_id __maybe_unused dp83822_tbl[] = { + { DP83822_PHY_ID, 0xfffffff0 }, + { DP83825I_PHY_ID, 0xfffffff0 }, + { DP83826C_PHY_ID, 0xfffffff0 }, +--- a/drivers/net/phy/dp83848.c ++++ b/drivers/net/phy/dp83848.c +@@ -123,7 +123,7 @@ static int dp83848_config_init(struct ph + return 0; + } + +-static struct mdio_device_id __maybe_unused dp83848_tbl[] = { ++static const struct mdio_device_id __maybe_unused dp83848_tbl[] = { + { TI_DP83848C_PHY_ID, 0xfffffff0 }, + { NS_DP83848C_PHY_ID, 0xfffffff0 }, + { TI_DP83620_PHY_ID, 0xfffffff0 }, +--- a/drivers/net/phy/dp83867.c ++++ b/drivers/net/phy/dp83867.c +@@ -1210,7 +1210,7 @@ static struct phy_driver dp83867_driver[ + }; + module_phy_driver(dp83867_driver); + +-static struct mdio_device_id __maybe_unused dp83867_tbl[] = { ++static const struct mdio_device_id __maybe_unused dp83867_tbl[] = { + { DP83867_PHY_ID, 0xfffffff0 }, + { } + }; +--- a/drivers/net/phy/dp83869.c ++++ b/drivers/net/phy/dp83869.c +@@ -928,7 +928,7 @@ static struct phy_driver dp83869_driver[ + }; + module_phy_driver(dp83869_driver); + +-static struct mdio_device_id __maybe_unused dp83869_tbl[] = { ++static const struct mdio_device_id __maybe_unused dp83869_tbl[] = { + { PHY_ID_MATCH_MODEL(DP83869_PHY_ID) }, + { PHY_ID_MATCH_MODEL(DP83561_PHY_ID) }, + { } +--- a/drivers/net/phy/dp83tc811.c ++++ b/drivers/net/phy/dp83tc811.c +@@ -403,7 +403,7 @@ static struct phy_driver dp83811_driver[ + }; + module_phy_driver(dp83811_driver); + +-static struct mdio_device_id __maybe_unused dp83811_tbl[] = { ++static const struct mdio_device_id __maybe_unused dp83811_tbl[] = { + { DP83TC811_PHY_ID, 0xfffffff0 }, + { }, + }; +--- a/drivers/net/phy/dp83td510.c ++++ b/drivers/net/phy/dp83td510.c +@@ -605,7 +605,7 @@ static struct phy_driver dp83td510_drive + } }; + module_phy_driver(dp83td510_driver); + +-static struct mdio_device_id __maybe_unused dp83td510_tbl[] = { ++static const struct mdio_device_id __maybe_unused dp83td510_tbl[] = { + { PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID) }, + { } + }; +--- a/drivers/net/phy/dp83tg720.c ++++ b/drivers/net/phy/dp83tg720.c +@@ -361,7 +361,7 @@ static struct phy_driver dp83tg720_drive + } }; + module_phy_driver(dp83tg720_driver); + +-static struct mdio_device_id __maybe_unused dp83tg720_tbl[] = { ++static const struct mdio_device_id __maybe_unused dp83tg720_tbl[] = { + { PHY_ID_MATCH_MODEL(DP83TG720S_PHY_ID) }, + { } + }; +--- a/drivers/net/phy/et1011c.c ++++ b/drivers/net/phy/et1011c.c +@@ -94,7 +94,7 @@ static struct phy_driver et1011c_driver[ + + module_phy_driver(et1011c_driver); + +-static struct mdio_device_id __maybe_unused et1011c_tbl[] = { ++static const struct mdio_device_id __maybe_unused et1011c_tbl[] = { + { 0x0282f014, 0xfffffff0 }, + { } + }; +--- a/drivers/net/phy/icplus.c ++++ b/drivers/net/phy/icplus.c +@@ -624,7 +624,7 @@ static struct phy_driver icplus_driver[] + + module_phy_driver(icplus_driver); + +-static struct mdio_device_id __maybe_unused icplus_tbl[] = { ++static const struct mdio_device_id __maybe_unused icplus_tbl[] = { + { PHY_ID_MATCH_MODEL(IP175C_PHY_ID) }, + { PHY_ID_MATCH_MODEL(IP1001_PHY_ID) }, + { PHY_ID_MATCH_EXACT(IP101A_PHY_ID) }, +--- a/drivers/net/phy/intel-xway.c ++++ b/drivers/net/phy/intel-xway.c +@@ -456,7 +456,7 @@ static struct phy_driver xway_gphy[] = { + }; + module_phy_driver(xway_gphy); + +-static struct mdio_device_id __maybe_unused xway_gphy_tbl[] = { ++static const struct mdio_device_id __maybe_unused xway_gphy_tbl[] = { + { PHY_ID_PHY11G_1_3, 0xffffffff }, + { PHY_ID_PHY22F_1_3, 0xffffffff }, + { PHY_ID_PHY11G_1_4, 0xffffffff }, +--- a/drivers/net/phy/lxt.c ++++ b/drivers/net/phy/lxt.c +@@ -348,7 +348,7 @@ static struct phy_driver lxt97x_driver[] + + module_phy_driver(lxt97x_driver); + +-static struct mdio_device_id __maybe_unused lxt_tbl[] = { ++static const struct mdio_device_id __maybe_unused lxt_tbl[] = { + { 0x78100000, 0xfffffff0 }, + { 0x001378e0, 0xfffffff0 }, + { 0x00137a10, 0xfffffff0 }, +--- a/drivers/net/phy/marvell-88q2xxx.c ++++ b/drivers/net/phy/marvell-88q2xxx.c +@@ -940,7 +940,7 @@ static struct phy_driver mv88q2xxx_drive + + module_phy_driver(mv88q2xxx_driver); + +-static struct mdio_device_id __maybe_unused mv88q2xxx_tbl[] = { ++static const struct mdio_device_id __maybe_unused mv88q2xxx_tbl[] = { + { MARVELL_PHY_ID_88Q2110, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88Q2220, MARVELL_PHY_ID_MASK }, + { /*sentinel*/ } +--- a/drivers/net/phy/marvell-88x2222.c ++++ b/drivers/net/phy/marvell-88x2222.c +@@ -613,7 +613,7 @@ static struct phy_driver mv2222_drivers[ + }; + module_phy_driver(mv2222_drivers); + +-static struct mdio_device_id __maybe_unused mv2222_tbl[] = { ++static const struct mdio_device_id __maybe_unused mv2222_tbl[] = { + { MARVELL_PHY_ID_88X2222, MARVELL_PHY_ID_MASK }, + { } + }; +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -4218,7 +4218,7 @@ static struct phy_driver marvell_drivers + + module_phy_driver(marvell_drivers); + +-static struct mdio_device_id __maybe_unused marvell_tbl[] = { ++static const struct mdio_device_id __maybe_unused marvell_tbl[] = { + { MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E3082, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK }, +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -1484,7 +1484,7 @@ static struct phy_driver mv3310_drivers[ + + module_phy_driver(mv3310_drivers); + +-static struct mdio_device_id __maybe_unused mv3310_tbl[] = { ++static const struct mdio_device_id __maybe_unused mv3310_tbl[] = { + { MARVELL_PHY_ID_88X3310, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E2110, MARVELL_PHY_ID_MASK }, + { }, +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -1356,7 +1356,7 @@ static struct phy_driver mtk_socphy_driv + + module_phy_driver(mtk_socphy_driver); + +-static struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = { ++static const struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = { + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981) }, + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988) }, + { } +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -93,7 +93,7 @@ static struct phy_driver mtk_gephy_drive + + module_phy_driver(mtk_gephy_driver); + +-static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { ++static const struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530) }, + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7531) }, + { } +--- a/drivers/net/phy/meson-gxl.c ++++ b/drivers/net/phy/meson-gxl.c +@@ -221,7 +221,7 @@ static struct phy_driver meson_gxl_phy[] + }, + }; + +-static struct mdio_device_id __maybe_unused meson_gxl_tbl[] = { ++static const struct mdio_device_id __maybe_unused meson_gxl_tbl[] = { + { PHY_ID_MATCH_VENDOR(0x01814400) }, + { PHY_ID_MATCH_VENDOR(0x01803301) }, + { } +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -5835,7 +5835,7 @@ MODULE_DESCRIPTION("Micrel PHY driver"); + MODULE_AUTHOR("David J. Choi"); + MODULE_LICENSE("GPL"); + +-static struct mdio_device_id __maybe_unused micrel_tbl[] = { ++static const struct mdio_device_id __maybe_unused micrel_tbl[] = { + { PHY_ID_KSZ9021, 0x000ffffe }, + { PHY_ID_KSZ9031, MICREL_PHY_ID_MASK }, + { PHY_ID_KSZ9131, MICREL_PHY_ID_MASK }, +--- a/drivers/net/phy/microchip.c ++++ b/drivers/net/phy/microchip.c +@@ -509,7 +509,7 @@ static struct phy_driver microchip_phy_d + + module_phy_driver(microchip_phy_driver); + +-static struct mdio_device_id __maybe_unused microchip_tbl[] = { ++static const struct mdio_device_id __maybe_unused microchip_tbl[] = { + { 0x0007c132, 0xfffffff2 }, + { PHY_ID_MATCH_MODEL(PHY_ID_LAN937X_TX) }, + { } +--- a/drivers/net/phy/microchip_t1.c ++++ b/drivers/net/phy/microchip_t1.c +@@ -1886,7 +1886,7 @@ static struct phy_driver microchip_t1_ph + + module_phy_driver(microchip_t1_phy_driver); + +-static struct mdio_device_id __maybe_unused microchip_t1_tbl[] = { ++static const struct mdio_device_id __maybe_unused microchip_t1_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_LAN87XX) }, + { PHY_ID_MATCH_MODEL(PHY_ID_LAN937X) }, + { PHY_ID_MATCH_MODEL(PHY_ID_LAN887X) }, +--- a/drivers/net/phy/microchip_t1s.c ++++ b/drivers/net/phy/microchip_t1s.c +@@ -323,7 +323,7 @@ static struct phy_driver microchip_t1s_d + + module_phy_driver(microchip_t1s_driver); + +-static struct mdio_device_id __maybe_unused tbl[] = { ++static const struct mdio_device_id __maybe_unused tbl[] = { + { PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1) }, + { PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB0) }, + { } +--- a/drivers/net/phy/mscc/mscc_main.c ++++ b/drivers/net/phy/mscc/mscc_main.c +@@ -2710,7 +2710,7 @@ static struct phy_driver vsc85xx_driver[ + + module_phy_driver(vsc85xx_driver); + +-static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = { ++static const struct mdio_device_id __maybe_unused vsc85xx_tbl[] = { + { PHY_ID_MATCH_VENDOR(PHY_VENDOR_MSCC) }, + { } + }; +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -1047,7 +1047,7 @@ static struct phy_driver gpy_drivers[] = + }; + module_phy_driver(gpy_drivers); + +-static struct mdio_device_id __maybe_unused gpy_tbl[] = { ++static const struct mdio_device_id __maybe_unused gpy_tbl[] = { + {PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)}, + {PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK}, + {PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)}, +--- a/drivers/net/phy/national.c ++++ b/drivers/net/phy/national.c +@@ -173,7 +173,7 @@ MODULE_DESCRIPTION("NatSemi PHY driver") + MODULE_AUTHOR("Stuart Menefy"); + MODULE_LICENSE("GPL"); + +-static struct mdio_device_id __maybe_unused ns_tbl[] = { ++static const struct mdio_device_id __maybe_unused ns_tbl[] = { + { DP83865_PHY_ID, 0xfffffff0 }, + { } + }; +--- a/drivers/net/phy/ncn26000.c ++++ b/drivers/net/phy/ncn26000.c +@@ -159,7 +159,7 @@ static struct phy_driver ncn26000_driver + + module_phy_driver(ncn26000_driver); + +-static struct mdio_device_id __maybe_unused ncn26000_tbl[] = { ++static const struct mdio_device_id __maybe_unused ncn26000_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_NCN26000) }, + { } + }; +--- a/drivers/net/phy/nxp-c45-tja11xx.c ++++ b/drivers/net/phy/nxp-c45-tja11xx.c +@@ -2102,7 +2102,7 @@ static struct phy_driver nxp_c45_driver[ + + module_phy_driver(nxp_c45_driver); + +-static struct mdio_device_id __maybe_unused nxp_c45_tbl[] = { ++static const struct mdio_device_id __maybe_unused nxp_c45_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_TJA_1103) }, + { PHY_ID_MATCH_MODEL(PHY_ID_TJA_1120) }, + { /*sentinel*/ }, +--- a/drivers/net/phy/nxp-cbtx.c ++++ b/drivers/net/phy/nxp-cbtx.c +@@ -215,7 +215,7 @@ static struct phy_driver cbtx_driver[] = + + module_phy_driver(cbtx_driver); + +-static struct mdio_device_id __maybe_unused cbtx_tbl[] = { ++static const struct mdio_device_id __maybe_unused cbtx_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_CBTX_SJA1110) }, + { }, + }; +--- a/drivers/net/phy/nxp-tja11xx.c ++++ b/drivers/net/phy/nxp-tja11xx.c +@@ -888,7 +888,7 @@ static struct phy_driver tja11xx_driver[ + + module_phy_driver(tja11xx_driver); + +-static struct mdio_device_id __maybe_unused tja11xx_tbl[] = { ++static const struct mdio_device_id __maybe_unused tja11xx_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_TJA1100) }, + { PHY_ID_MATCH_MODEL(PHY_ID_TJA1101) }, + { PHY_ID_MATCH_MODEL(PHY_ID_TJA1102) }, +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -1071,7 +1071,7 @@ static struct phy_driver at803x_driver[] + + module_phy_driver(at803x_driver); + +-static struct mdio_device_id __maybe_unused atheros_tbl[] = { ++static const struct mdio_device_id __maybe_unused atheros_tbl[] = { + { ATH8030_PHY_ID, AT8030_PHY_ID_MASK }, + { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) }, + { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, +--- a/drivers/net/phy/qcom/qca807x.c ++++ b/drivers/net/phy/qcom/qca807x.c +@@ -828,7 +828,7 @@ static struct phy_driver qca807x_drivers + }; + module_phy_driver(qca807x_drivers); + +-static struct mdio_device_id __maybe_unused qca807x_tbl[] = { ++static const struct mdio_device_id __maybe_unused qca807x_tbl[] = { + { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) }, + { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) }, + { } +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -655,7 +655,7 @@ static struct phy_driver qca808x_driver[ + + module_phy_driver(qca808x_driver); + +-static struct mdio_device_id __maybe_unused qca808x_tbl[] = { ++static const struct mdio_device_id __maybe_unused qca808x_tbl[] = { + { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, + { } + }; +--- a/drivers/net/phy/qcom/qca83xx.c ++++ b/drivers/net/phy/qcom/qca83xx.c +@@ -261,7 +261,7 @@ static struct phy_driver qca83xx_driver[ + + module_phy_driver(qca83xx_driver); + +-static struct mdio_device_id __maybe_unused qca83xx_tbl[] = { ++static const struct mdio_device_id __maybe_unused qca83xx_tbl[] = { + { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, +--- a/drivers/net/phy/qsemi.c ++++ b/drivers/net/phy/qsemi.c +@@ -155,7 +155,7 @@ static struct phy_driver qs6612_driver[] + + module_phy_driver(qs6612_driver); + +-static struct mdio_device_id __maybe_unused qs6612_tbl[] = { ++static const struct mdio_device_id __maybe_unused qs6612_tbl[] = { + { 0x00181440, 0xfffffff0 }, + { } + }; +--- a/drivers/net/phy/rockchip.c ++++ b/drivers/net/phy/rockchip.c +@@ -188,7 +188,7 @@ static struct phy_driver rockchip_phy_dr + + module_phy_driver(rockchip_phy_driver); + +-static struct mdio_device_id __maybe_unused rockchip_phy_tbl[] = { ++static const struct mdio_device_id __maybe_unused rockchip_phy_tbl[] = { + { INTERNAL_EPHY_ID, 0xfffffff0 }, + { } + }; +--- a/drivers/net/phy/smsc.c ++++ b/drivers/net/phy/smsc.c +@@ -885,7 +885,7 @@ MODULE_DESCRIPTION("SMSC PHY driver"); + MODULE_AUTHOR("Herbert Valerio Riedel"); + MODULE_LICENSE("GPL"); + +-static struct mdio_device_id __maybe_unused smsc_tbl[] = { ++static const struct mdio_device_id __maybe_unused smsc_tbl[] = { + { 0x0007c0a0, 0xfffffff0 }, + { 0x0007c0b0, 0xfffffff0 }, + { 0x0007c0c0, 0xfffffff0 }, +--- a/drivers/net/phy/ste10Xp.c ++++ b/drivers/net/phy/ste10Xp.c +@@ -124,7 +124,7 @@ static struct phy_driver ste10xp_pdriver + + module_phy_driver(ste10xp_pdriver); + +-static struct mdio_device_id __maybe_unused ste10Xp_tbl[] = { ++static const struct mdio_device_id __maybe_unused ste10Xp_tbl[] = { + { STE101P_PHY_ID, 0xfffffff0 }, + { STE100P_PHY_ID, 0xffffffff }, + { } +--- a/drivers/net/phy/teranetics.c ++++ b/drivers/net/phy/teranetics.c +@@ -87,7 +87,7 @@ static struct phy_driver teranetics_driv + + module_phy_driver(teranetics_driver); + +-static struct mdio_device_id __maybe_unused teranetics_tbl[] = { ++static const struct mdio_device_id __maybe_unused teranetics_tbl[] = { + { PHY_ID_TN2020, 0xffffffff }, + { } + }; +--- a/drivers/net/phy/uPD60620.c ++++ b/drivers/net/phy/uPD60620.c +@@ -90,7 +90,7 @@ static struct phy_driver upd60620_driver + + module_phy_driver(upd60620_driver); + +-static struct mdio_device_id __maybe_unused upd60620_tbl[] = { ++static const struct mdio_device_id __maybe_unused upd60620_tbl[] = { + { UPD60620_PHY_ID, 0xfffffffe }, + { } + }; +--- a/drivers/net/phy/vitesse.c ++++ b/drivers/net/phy/vitesse.c +@@ -674,7 +674,7 @@ static struct phy_driver vsc82xx_driver[ + + module_phy_driver(vsc82xx_driver); + +-static struct mdio_device_id __maybe_unused vitesse_tbl[] = { ++static const struct mdio_device_id __maybe_unused vitesse_tbl[] = { + { PHY_ID_VSC8234, 0x000ffff0 }, + { PHY_ID_VSC8244, 0x000fffc0 }, + { PHY_ID_VSC8572, 0x000ffff0 }, diff --git a/target/linux/generic/backport-6.12/720-10-v6.15-net-phy-mediatek-Change-to-more-meaningful-macros.patch b/target/linux/generic/backport-6.12/720-10-v6.15-net-phy-mediatek-Change-to-more-meaningful-macros.patch new file mode 100644 index 00000000000..545e92987f0 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-10-v6.15-net-phy-mediatek-Change-to-more-meaningful-macros.patch @@ -0,0 +1,146 @@ +From 7e06c3dbfa5f1e39eba92eb79d854fab2a7ad5fe Mon Sep 17 00:00:00 2001 +From: Sky Huang +Date: Thu, 13 Feb 2025 16:05:49 +0800 +Subject: [PATCH 10/20] net: phy: mediatek: Change to more meaningful macros + +Replace magic number with more meaningful macros in mtk-ge.c. +Also, move some common macros into mtk-phy-lib.c. + +Signed-off-by: Sky Huang +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250213080553.921434-2-SkyLake.Huang@mediatek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge-soc.c | 1 - + drivers/net/phy/mediatek/mtk-ge.c | 71 +++++++++++++++++++++------ + drivers/net/phy/mediatek/mtk.h | 2 + + 3 files changed, 57 insertions(+), 17 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -24,7 +24,6 @@ + #define MTK_PHY_SMI_DET_ON_THRESH_MASK GENMASK(13, 8) + + #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +-#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + + #define ANALOG_INTERNAL_OPERATION_MAX_US 20 + #define TXRESERVE_MIN 0 +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -8,18 +8,38 @@ + #define MTK_GPHY_ID_MT7530 0x03a29412 + #define MTK_GPHY_ID_MT7531 0x03a29441 + +-#define MTK_EXT_PAGE_ACCESS 0x1f +-#define MTK_PHY_PAGE_STANDARD 0x0000 +-#define MTK_PHY_PAGE_EXTENDED 0x0001 +-#define MTK_PHY_PAGE_EXTENDED_2 0x0002 +-#define MTK_PHY_PAGE_EXTENDED_3 0x0003 +-#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +-#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 ++#define MTK_PHY_PAGE_EXTENDED_1 0x0001 ++#define MTK_PHY_AUX_CTRL_AND_STATUS 0x14 ++#define MTK_PHY_ENABLE_DOWNSHIFT BIT(4) ++ ++#define MTK_PHY_PAGE_EXTENDED_2 0x0002 ++#define MTK_PHY_PAGE_EXTENDED_3 0x0003 ++#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG11 0x11 ++ ++#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 ++ ++/* Registers on MDIO_MMD_VEND1 */ ++#define MTK_PHY_GBE_MODE_TX_DELAY_SEL 0x13 ++#define MTK_PHY_TEST_MODE_TX_DELAY_SEL 0x14 ++#define MTK_TX_DELAY_PAIR_B_MASK GENMASK(10, 8) ++#define MTK_TX_DELAY_PAIR_D_MASK GENMASK(2, 0) ++ ++#define MTK_PHY_MCC_CTRL_AND_TX_POWER_CTRL 0xa6 ++#define MTK_MCC_NEARECHO_OFFSET_MASK GENMASK(15, 8) ++ ++#define MTK_PHY_RXADC_CTRL_RG7 0xc6 ++#define MTK_PHY_DA_AD_BUF_BIAS_LP_MASK GENMASK(9, 8) ++ ++#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG123 0x123 ++#define MTK_PHY_LPI_NORM_MSE_LO_THRESH100_MASK GENMASK(15, 8) ++#define MTK_PHY_LPI_NORM_MSE_HI_THRESH100_MASK GENMASK(7, 0) + + static void mtk_gephy_config_init(struct phy_device *phydev) + { + /* Enable HW auto downshift */ +- phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4)); ++ phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED_1, ++ MTK_PHY_AUX_CTRL_AND_STATUS, ++ 0, MTK_PHY_ENABLE_DOWNSHIFT); + + /* Increase SlvDPSready time */ + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +@@ -29,10 +49,20 @@ static void mtk_gephy_config_init(struct + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* Adjust 100_mse_threshold */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff); +- +- /* Disable mcc */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG123, ++ MTK_PHY_LPI_NORM_MSE_LO_THRESH100_MASK | ++ MTK_PHY_LPI_NORM_MSE_HI_THRESH100_MASK, ++ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_LO_THRESH100_MASK, ++ 0xff) | ++ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH100_MASK, ++ 0xff)); ++ ++ /* If echo time is narrower than 0x3, it will be regarded as noise */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_MCC_CTRL_AND_TX_POWER_CTRL, ++ MTK_MCC_NEARECHO_OFFSET_MASK, ++ FIELD_PREP(MTK_MCC_NEARECHO_OFFSET_MASK, 0x3)); + } + + static int mt7530_phy_config_init(struct phy_device *phydev) +@@ -40,7 +70,8 @@ static int mt7530_phy_config_init(struct + mtk_gephy_config_init(phydev); + + /* Increase post_update_timer */ +- phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b); ++ phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, ++ MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG11, 0x4b); + + return 0; + } +@@ -51,11 +82,19 @@ static int mt7531_phy_config_init(struct + + /* PHY link down power saving enable */ + phy_set_bits(phydev, 0x17, BIT(4)); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, ++ MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, ++ FIELD_PREP(MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3)); + + /* Set TX Pair delay selection */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_GBE_MODE_TX_DELAY_SEL, ++ MTK_TX_DELAY_PAIR_B_MASK | MTK_TX_DELAY_PAIR_D_MASK, ++ FIELD_PREP(MTK_TX_DELAY_PAIR_B_MASK, 0x4) | ++ FIELD_PREP(MTK_TX_DELAY_PAIR_D_MASK, 0x4)); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TEST_MODE_TX_DELAY_SEL, ++ MTK_TX_DELAY_PAIR_B_MASK | MTK_TX_DELAY_PAIR_D_MASK, ++ FIELD_PREP(MTK_TX_DELAY_PAIR_B_MASK, 0x4) | ++ FIELD_PREP(MTK_TX_DELAY_PAIR_D_MASK, 0x4)); + + return 0; + } +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -9,6 +9,8 @@ + #define _MTK_EPHY_H_ + + #define MTK_EXT_PAGE_ACCESS 0x1f ++#define MTK_PHY_PAGE_STANDARD 0x0000 ++#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + + /* Registers on MDIO_MMD_VEND2 */ + #define MTK_PHY_LED0_ON_CTRL 0x24 diff --git a/target/linux/generic/backport-6.12/720-11-v6.15-net-phy-mediatek-Add-token-ring-access-helper-functi.patch b/target/linux/generic/backport-6.12/720-11-v6.15-net-phy-mediatek-Add-token-ring-access-helper-functi.patch new file mode 100644 index 00000000000..40ce29fc51a --- /dev/null +++ b/target/linux/generic/backport-6.12/720-11-v6.15-net-phy-mediatek-Add-token-ring-access-helper-functi.patch @@ -0,0 +1,448 @@ +From 6e7370079669b0d55c9464bb7c3fb8fb7368b912 Mon Sep 17 00:00:00 2001 +From: Sky Huang +Date: Thu, 13 Feb 2025 16:05:50 +0800 +Subject: [PATCH 11/20] net: phy: mediatek: Add token ring access helper + functions in mtk-phy-lib + +This patch adds TR(token ring) manipulations and adds correct +macro names for those magic numbers. TR is a way to access +proprietary registers on page 52b5. Use these helper functions +so we can see which fields we're going to modify/set/clear. + +TR functions with __* prefix mean that the operations inside +aren't wrapped by page select/restore functions. + +This patch doesn't really change registers' settings but just +enhances readability and maintainability. + +Signed-off-by: Sky Huang +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250213080553.921434-3-SkyLake.Huang@mediatek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge-soc.c | 231 +++++++++++++++++-------- + drivers/net/phy/mediatek/mtk-ge.c | 11 +- + drivers/net/phy/mediatek/mtk-phy-lib.c | 63 +++++++ + drivers/net/phy/mediatek/mtk.h | 5 + + 4 files changed, 230 insertions(+), 80 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -25,6 +25,90 @@ + + #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 + ++/* Registers on Token Ring debug nodes */ ++/* ch_addr = 0x0, node_addr = 0x7, data_addr = 0x15 */ ++/* NormMseLoThresh */ ++#define NORMAL_MSE_LO_THRESH_MASK GENMASK(15, 8) ++ ++/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */ ++/* RemAckCntLimitCtrl */ ++#define REMOTE_ACK_COUNT_LIMIT_CTRL_MASK GENMASK(2, 1) ++ ++/* ch_addr = 0x1, node_addr = 0xd, data_addr = 0x20 */ ++/* VcoSlicerThreshBitsHigh */ ++#define VCO_SLICER_THRESH_HIGH_MASK GENMASK(23, 0) ++ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x0 */ ++/* DfeTailEnableVgaThresh1000 */ ++#define DFE_TAIL_EANBLE_VGA_TRHESH_1000 GENMASK(5, 1) ++ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x1 */ ++/* MrvlTrFix100Kp */ ++#define MRVL_TR_FIX_100KP_MASK GENMASK(22, 20) ++/* MrvlTrFix100Kf */ ++#define MRVL_TR_FIX_100KF_MASK GENMASK(19, 17) ++/* MrvlTrFix1000Kp */ ++#define MRVL_TR_FIX_1000KP_MASK GENMASK(16, 14) ++/* MrvlTrFix1000Kf */ ++#define MRVL_TR_FIX_1000KF_MASK GENMASK(13, 11) ++ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x12 */ ++/* VgaDecRate */ ++#define VGA_DECIMATION_RATE_MASK GENMASK(8, 5) ++ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x17 */ ++/* SlvDSPreadyTime */ ++#define SLAVE_DSP_READY_TIME_MASK GENMASK(22, 15) ++/* MasDSPreadyTime */ ++#define MASTER_DSP_READY_TIME_MASK GENMASK(14, 7) ++ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x20 */ ++/* ResetSyncOffset */ ++#define RESET_SYNC_OFFSET_MASK GENMASK(11, 8) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x0 */ ++/* FfeUpdGainForceVal */ ++#define FFE_UPDATE_GAIN_FORCE_VAL_MASK GENMASK(9, 7) ++/* FfeUpdGainForce */ ++#define FFE_UPDATE_GAIN_FORCE BIT(6) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x6 */ ++/* SS: Steady-state, KP: Proportional Gain */ ++/* SSTrKp100 */ ++#define SS_TR_KP100_MASK GENMASK(21, 19) ++/* SSTrKf100 */ ++#define SS_TR_KF100_MASK GENMASK(18, 16) ++/* SSTrKp1000Mas */ ++#define SS_TR_KP1000_MASTER_MASK GENMASK(15, 13) ++/* SSTrKf1000Mas */ ++#define SS_TR_KF1000_MASTER_MASK GENMASK(12, 10) ++/* SSTrKp1000Slv */ ++#define SS_TR_KP1000_SLAVE_MASK GENMASK(9, 7) ++/* SSTrKf1000Slv */ ++#define SS_TR_KF1000_SLAVE_MASK GENMASK(6, 4) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0xd */ ++/* RegEEE_st2TrKf1000 */ ++#define EEE1000_STAGE2_TR_KF_MASK GENMASK(13, 11) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0xf */ ++/* RegEEE_slv_waketr_timer_tar */ ++#define SLAVE_WAKETR_TIMER_MASK GENMASK(20, 11) ++/* RegEEE_slv_remtx_timer_tar */ ++#define SLAVE_REMTX_TIMER_MASK GENMASK(10, 1) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x10 */ ++/* RegEEE_slv_wake_int_timer_tar */ ++#define SLAVE_WAKEINT_TIMER_MASK GENMASK(10, 1) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x14 */ ++/* RegEEE_trfreeze_timer2 */ ++#define TR_FREEZE_TIMER2_MASK GENMASK(9, 0) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x1c */ ++/* RegEEE100Stg1_tar */ ++#define EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK GENMASK(8, 0) ++ + #define ANALOG_INTERNAL_OPERATION_MAX_US 20 + #define TXRESERVE_MIN 0 + #define TXRESERVE_MAX 7 +@@ -700,40 +784,41 @@ restore: + static void mt798x_phy_common_finetune(struct phy_device *phydev) + { + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ +- __phy_write(phydev, 0x11, 0xc71); +- __phy_write(phydev, 0x12, 0xc); +- __phy_write(phydev, 0x10, 0x8fae); ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x17, ++ SLAVE_DSP_READY_TIME_MASK | MASTER_DSP_READY_TIME_MASK, ++ FIELD_PREP(SLAVE_DSP_READY_TIME_MASK, 0x18) | ++ FIELD_PREP(MASTER_DSP_READY_TIME_MASK, 0x18)); + + /* EnabRandUpdTrig = 1 */ + __phy_write(phydev, 0x11, 0x2f00); + __phy_write(phydev, 0x12, 0xe); + __phy_write(phydev, 0x10, 0x8fb0); + +- /* NormMseLoThresh = 85 */ +- __phy_write(phydev, 0x11, 0x55a0); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x83aa); +- +- /* FfeUpdGainForce = 1(Enable), FfeUpdGainForceVal = 4 */ +- __phy_write(phydev, 0x11, 0x240); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9680); ++ __mtk_tr_modify(phydev, 0x0, 0x7, 0x15, ++ NORMAL_MSE_LO_THRESH_MASK, ++ FIELD_PREP(NORMAL_MSE_LO_THRESH_MASK, 0x55)); ++ ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0x0, ++ FFE_UPDATE_GAIN_FORCE_VAL_MASK, ++ FIELD_PREP(FFE_UPDATE_GAIN_FORCE_VAL_MASK, 0x4) | ++ FFE_UPDATE_GAIN_FORCE); + + /* TrFreeze = 0 (mt7988 default) */ + __phy_write(phydev, 0x11, 0x0); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9686); + +- /* SSTrKp100 = 5 */ +- /* SSTrKf100 = 6 */ +- /* SSTrKp1000Mas = 5 */ +- /* SSTrKf1000Mas = 6 */ +- /* SSTrKp1000Slv = 5 */ +- /* SSTrKf1000Slv = 6 */ +- __phy_write(phydev, 0x11, 0xbaef); +- __phy_write(phydev, 0x12, 0x2e); +- __phy_write(phydev, 0x10, 0x968c); ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0x6, ++ SS_TR_KP100_MASK | SS_TR_KF100_MASK | ++ SS_TR_KP1000_MASTER_MASK | SS_TR_KF1000_MASTER_MASK | ++ SS_TR_KP1000_SLAVE_MASK | SS_TR_KF1000_SLAVE_MASK, ++ FIELD_PREP(SS_TR_KP100_MASK, 0x5) | ++ FIELD_PREP(SS_TR_KF100_MASK, 0x6) | ++ FIELD_PREP(SS_TR_KP1000_MASTER_MASK, 0x5) | ++ FIELD_PREP(SS_TR_KF1000_MASTER_MASK, 0x6) | ++ FIELD_PREP(SS_TR_KP1000_SLAVE_MASK, 0x5) | ++ FIELD_PREP(SS_TR_KF1000_SLAVE_MASK, 0x6)); ++ + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + } + +@@ -756,27 +841,29 @@ static void mt7981_phy_finetune(struct p + } + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* ResetSyncOffset = 6 */ +- __phy_write(phydev, 0x11, 0x600); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8fc0); +- +- /* VgaDecRate = 1 */ +- __phy_write(phydev, 0x11, 0x4c2a); +- __phy_write(phydev, 0x12, 0x3e); +- __phy_write(phydev, 0x10, 0x8fa4); ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x20, ++ RESET_SYNC_OFFSET_MASK, ++ FIELD_PREP(RESET_SYNC_OFFSET_MASK, 0x6)); ++ ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x12, ++ VGA_DECIMATION_RATE_MASK, ++ FIELD_PREP(VGA_DECIMATION_RATE_MASK, 0x1)); + + /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, + * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 + */ +- __phy_write(phydev, 0x11, 0xd10a); +- __phy_write(phydev, 0x12, 0x34); +- __phy_write(phydev, 0x10, 0x8f82); ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x1, ++ MRVL_TR_FIX_100KP_MASK | MRVL_TR_FIX_100KF_MASK | ++ MRVL_TR_FIX_1000KP_MASK | MRVL_TR_FIX_1000KF_MASK, ++ FIELD_PREP(MRVL_TR_FIX_100KP_MASK, 0x3) | ++ FIELD_PREP(MRVL_TR_FIX_100KF_MASK, 0x2) | ++ FIELD_PREP(MRVL_TR_FIX_1000KP_MASK, 0x3) | ++ FIELD_PREP(MRVL_TR_FIX_1000KF_MASK, 0x2)); + + /* VcoSlicerThreshBitsHigh */ +- __phy_write(phydev, 0x11, 0x5555); +- __phy_write(phydev, 0x12, 0x55); +- __phy_write(phydev, 0x10, 0x8ec0); ++ __mtk_tr_modify(phydev, 0x1, 0xd, 0x20, ++ VCO_SLICER_THRESH_HIGH_MASK, ++ FIELD_PREP(VCO_SLICER_THRESH_HIGH_MASK, 0x555555)); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */ +@@ -828,25 +915,23 @@ static void mt7988_phy_finetune(struct p + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* ResetSyncOffset = 5 */ +- __phy_write(phydev, 0x11, 0x500); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8fc0); ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x20, ++ RESET_SYNC_OFFSET_MASK, ++ FIELD_PREP(RESET_SYNC_OFFSET_MASK, 0x5)); + + /* VgaDecRate is 1 at default on mt7988 */ + +- /* MrvlTrFix100Kp = 6, MrvlTrFix100Kf = 7, +- * MrvlTrFix1000Kp = 6, MrvlTrFix1000Kf = 7 +- */ +- __phy_write(phydev, 0x11, 0xb90a); +- __phy_write(phydev, 0x12, 0x6f); +- __phy_write(phydev, 0x10, 0x8f82); +- +- /* RemAckCntLimitCtrl = 1 */ +- __phy_write(phydev, 0x11, 0xfbba); +- __phy_write(phydev, 0x12, 0xc3); +- __phy_write(phydev, 0x10, 0x87f8); +- ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x1, ++ MRVL_TR_FIX_100KP_MASK | MRVL_TR_FIX_100KF_MASK | ++ MRVL_TR_FIX_1000KP_MASK | MRVL_TR_FIX_1000KF_MASK, ++ FIELD_PREP(MRVL_TR_FIX_100KP_MASK, 0x6) | ++ FIELD_PREP(MRVL_TR_FIX_100KF_MASK, 0x7) | ++ FIELD_PREP(MRVL_TR_FIX_1000KP_MASK, 0x6) | ++ FIELD_PREP(MRVL_TR_FIX_1000KF_MASK, 0x7)); ++ ++ __mtk_tr_modify(phydev, 0x0, 0xf, 0x3c, ++ REMOTE_ACK_COUNT_LIMIT_CTRL_MASK, ++ FIELD_PREP(REMOTE_ACK_COUNT_LIMIT_CTRL_MASK, 0x1)); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */ +@@ -927,40 +1012,36 @@ static void mt798x_phy_eee(struct phy_de + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9690); + +- /* REG_EEE_st2TrKf1000 = 2 */ +- __phy_write(phydev, 0x11, 0x114f); +- __phy_write(phydev, 0x12, 0x2); +- __phy_write(phydev, 0x10, 0x969a); +- +- /* RegEEE_slv_wake_tr_timer_tar = 6, RegEEE_slv_remtx_timer_tar = 20 */ +- __phy_write(phydev, 0x11, 0x3028); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x969e); +- +- /* RegEEE_slv_wake_int_timer_tar = 8 */ +- __phy_write(phydev, 0x11, 0x5010); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96a0); +- +- /* RegEEE_trfreeze_timer2 = 586 */ +- __phy_write(phydev, 0x11, 0x24a); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96a8); +- +- /* RegEEE100Stg1_tar = 16 */ +- __phy_write(phydev, 0x11, 0x3210); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96b8); ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0xd, ++ EEE1000_STAGE2_TR_KF_MASK, ++ FIELD_PREP(EEE1000_STAGE2_TR_KF_MASK, 0x2)); ++ ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0xf, ++ SLAVE_WAKETR_TIMER_MASK | SLAVE_REMTX_TIMER_MASK, ++ FIELD_PREP(SLAVE_WAKETR_TIMER_MASK, 0x6) | ++ FIELD_PREP(SLAVE_REMTX_TIMER_MASK, 0x14)); ++ ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0x10, ++ SLAVE_WAKEINT_TIMER_MASK, ++ FIELD_PREP(SLAVE_WAKEINT_TIMER_MASK, 0x8)); ++ ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0x14, ++ TR_FREEZE_TIMER2_MASK, ++ FIELD_PREP(TR_FREEZE_TIMER2_MASK, 0x24a)); ++ ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0x1c, ++ EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK, ++ FIELD_PREP(EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK, ++ 0x10)); + + /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */ + __phy_write(phydev, 0x11, 0x1463); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x96ca); + +- /* DfeTailEnableVgaThresh1000 = 27 */ +- __phy_write(phydev, 0x11, 0x36); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8f80); ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x0, ++ DFE_TAIL_EANBLE_VGA_TRHESH_1000, ++ FIELD_PREP(DFE_TAIL_EANBLE_VGA_TRHESH_1000, 0x1b)); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3); +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -18,6 +18,10 @@ + + #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 + ++/* Registers on Token Ring debug nodes */ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x17 */ ++#define SLAVE_DSP_READY_TIME_MASK GENMASK(22, 15) ++ + /* Registers on MDIO_MMD_VEND1 */ + #define MTK_PHY_GBE_MODE_TX_DELAY_SEL 0x13 + #define MTK_PHY_TEST_MODE_TX_DELAY_SEL 0x14 +@@ -42,11 +46,8 @@ static void mtk_gephy_config_init(struct + 0, MTK_PHY_ENABLE_DOWNSHIFT); + + /* Increase SlvDPSready time */ +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- __phy_write(phydev, 0x10, 0xafae); +- __phy_write(phydev, 0x12, 0x2f); +- __phy_write(phydev, 0x10, 0x8fae); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ mtk_tr_modify(phydev, 0x1, 0xf, 0x17, SLAVE_DSP_READY_TIME_MASK, ++ FIELD_PREP(SLAVE_DSP_READY_TIME_MASK, 0x5e)); + + /* Adjust 100_mse_threshold */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, +--- a/drivers/net/phy/mediatek/mtk-phy-lib.c ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -6,6 +6,69 @@ + + #include "mtk.h" + ++/* Difference between functions with mtk_tr* and __mtk_tr* prefixes is ++ * mtk_tr* functions: wrapped by page switching operations ++ * __mtk_tr* functions: no page switching operations ++ */ ++ ++static void __mtk_tr_access(struct phy_device *phydev, bool read, u8 ch_addr, ++ u8 node_addr, u8 data_addr) ++{ ++ u16 tr_cmd = BIT(15); /* bit 14 & 0 are reserved */ ++ ++ if (read) ++ tr_cmd |= BIT(13); ++ ++ tr_cmd |= (((ch_addr & 0x3) << 11) | ++ ((node_addr & 0xf) << 7) | ++ ((data_addr & 0x3f) << 1)); ++ dev_dbg(&phydev->mdio.dev, "tr_cmd: 0x%x\n", tr_cmd); ++ __phy_write(phydev, 0x10, tr_cmd); ++} ++ ++static void __mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u16 *tr_high, u16 *tr_low) ++{ ++ __mtk_tr_access(phydev, true, ch_addr, node_addr, data_addr); ++ *tr_low = __phy_read(phydev, 0x11); ++ *tr_high = __phy_read(phydev, 0x12); ++ dev_dbg(&phydev->mdio.dev, "tr_high read: 0x%x, tr_low read: 0x%x\n", ++ *tr_high, *tr_low); ++} ++ ++static void __mtk_tr_write(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 tr_data) ++{ ++ __phy_write(phydev, 0x11, tr_data & 0xffff); ++ __phy_write(phydev, 0x12, tr_data >> 16); ++ dev_dbg(&phydev->mdio.dev, "tr_high write: 0x%x, tr_low write: 0x%x\n", ++ tr_data >> 16, tr_data & 0xffff); ++ __mtk_tr_access(phydev, false, ch_addr, node_addr, data_addr); ++} ++ ++void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 mask, u32 set) ++{ ++ u32 tr_data; ++ u16 tr_high; ++ u16 tr_low; ++ ++ __mtk_tr_read(phydev, ch_addr, node_addr, data_addr, &tr_high, &tr_low); ++ tr_data = (tr_high << 16) | tr_low; ++ tr_data = (tr_data & ~mask) | set; ++ __mtk_tr_write(phydev, ch_addr, node_addr, data_addr, tr_data); ++} ++EXPORT_SYMBOL_GPL(__mtk_tr_modify); ++ ++void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 mask, u32 set) ++{ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, mask, set); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++} ++EXPORT_SYMBOL_GPL(mtk_tr_modify); ++ + int mtk_phy_read_page(struct phy_device *phydev) + { + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -68,6 +68,11 @@ struct mtk_socphy_priv { + unsigned long led_state; + }; + ++void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 mask, u32 set); ++void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 mask, u32 set); ++ + int mtk_phy_read_page(struct phy_device *phydev); + int mtk_phy_write_page(struct phy_device *phydev, int page); + diff --git a/target/linux/generic/backport-6.12/720-12-v6.15-net-phy-mediatek-Add-token-ring-set-bit-operation-su.patch b/target/linux/generic/backport-6.12/720-12-v6.15-net-phy-mediatek-Add-token-ring-set-bit-operation-su.patch new file mode 100644 index 00000000000..972a9c0a7e3 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-12-v6.15-net-phy-mediatek-Add-token-ring-set-bit-operation-su.patch @@ -0,0 +1,73 @@ +From c7e2fb3421ef5ebbb4c91f44bd735ab10edd755a Mon Sep 17 00:00:00 2001 +From: Sky Huang +Date: Thu, 13 Feb 2025 16:05:51 +0800 +Subject: [PATCH 12/20] net: phy: mediatek: Add token ring set bit operation + support + +Previously in mtk-ge-soc.c, we set some register bits via token +ring, which were implemented in three __phy_write(). +Now we can do the same thing via __mtk_tr_set_bits() helper. + +Signed-off-by: Sky Huang +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250213080553.921434-4-SkyLake.Huang@mediatek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge-soc.c | 10 ++++++---- + drivers/net/phy/mediatek/mtk-phy-lib.c | 7 +++++++ + drivers/net/phy/mediatek/mtk.h | 2 ++ + 3 files changed, 15 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -62,6 +62,10 @@ + /* MasDSPreadyTime */ + #define MASTER_DSP_READY_TIME_MASK GENMASK(14, 7) + ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x18 */ ++/* EnabRandUpdTrig */ ++#define ENABLE_RANDOM_UPDOWN_COUNTER_TRIGGER BIT(8) ++ + /* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x20 */ + /* ResetSyncOffset */ + #define RESET_SYNC_OFFSET_MASK GENMASK(11, 8) +@@ -789,10 +793,8 @@ static void mt798x_phy_common_finetune(s + FIELD_PREP(SLAVE_DSP_READY_TIME_MASK, 0x18) | + FIELD_PREP(MASTER_DSP_READY_TIME_MASK, 0x18)); + +- /* EnabRandUpdTrig = 1 */ +- __phy_write(phydev, 0x11, 0x2f00); +- __phy_write(phydev, 0x12, 0xe); +- __phy_write(phydev, 0x10, 0x8fb0); ++ __mtk_tr_set_bits(phydev, 0x1, 0xf, 0x18, ++ ENABLE_RANDOM_UPDOWN_COUNTER_TRIGGER); + + __mtk_tr_modify(phydev, 0x0, 0x7, 0x15, + NORMAL_MSE_LO_THRESH_MASK, +--- a/drivers/net/phy/mediatek/mtk-phy-lib.c ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -69,6 +69,13 @@ void mtk_tr_modify(struct phy_device *ph + } + EXPORT_SYMBOL_GPL(mtk_tr_modify); + ++void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 set) ++{ ++ __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, 0, set); ++} ++EXPORT_SYMBOL_GPL(__mtk_tr_set_bits); ++ + int mtk_phy_read_page(struct phy_device *phydev) + { + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -72,6 +72,8 @@ void __mtk_tr_modify(struct phy_device * + u8 data_addr, u32 mask, u32 set); + void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 mask, u32 set); ++void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 set); + + int mtk_phy_read_page(struct phy_device *phydev); + int mtk_phy_write_page(struct phy_device *phydev, int page); diff --git a/target/linux/generic/backport-6.12/720-13-v6.15-net-phy-mediatek-Add-token-ring-clear-bit-operation-.patch b/target/linux/generic/backport-6.12/720-13-v6.15-net-phy-mediatek-Add-token-ring-clear-bit-operation-.patch new file mode 100644 index 00000000000..47d891ea698 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-13-v6.15-net-phy-mediatek-Add-token-ring-clear-bit-operation-.patch @@ -0,0 +1,122 @@ +From 7851c73a416b15aff6f9ada9c88affc5f48ff011 Mon Sep 17 00:00:00 2001 +From: Sky Huang +Date: Thu, 13 Feb 2025 16:05:52 +0800 +Subject: [PATCH 13/20] net: phy: mediatek: Add token ring clear bit operation + support + +Similar to __mtk_tr_set_bits() support. Previously in mtk-ge-soc.c, +we clear some register bits via token ring, which were also implemented +in three __phy_write(). Now we can do the same thing via +__mtk_tr_clr_bits() helper. + +Signed-off-by: Sky Huang +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250213080553.921434-5-SkyLake.Huang@mediatek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge-soc.c | 30 +++++++++++++++----------- + drivers/net/phy/mediatek/mtk-phy-lib.c | 7 ++++++ + drivers/net/phy/mediatek/mtk.h | 2 ++ + 3 files changed, 27 insertions(+), 12 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -76,6 +76,10 @@ + /* FfeUpdGainForce */ + #define FFE_UPDATE_GAIN_FORCE BIT(6) + ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x3 */ ++/* TrFreeze */ ++#define TR_FREEZE_MASK GENMASK(11, 0) ++ + /* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x6 */ + /* SS: Steady-state, KP: Proportional Gain */ + /* SSTrKp100 */ +@@ -91,6 +95,11 @@ + /* SSTrKf1000Slv */ + #define SS_TR_KF1000_SLAVE_MASK GENMASK(6, 4) + ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x8 */ ++/* clear this bit if wanna select from AFE */ ++/* Regsigdet_sel_1000 */ ++#define EEE1000_SELECT_SIGNAL_DETECTION_FROM_DFE BIT(4) ++ + /* ch_addr = 0x2, node_addr = 0xd, data_addr = 0xd */ + /* RegEEE_st2TrKf1000 */ + #define EEE1000_STAGE2_TR_KF_MASK GENMASK(13, 11) +@@ -113,6 +122,10 @@ + /* RegEEE100Stg1_tar */ + #define EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK GENMASK(8, 0) + ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x25 */ ++/* REGEEE_wake_slv_tr_wait_dfesigdet_en */ ++#define WAKE_SLAVE_TR_WAIT_DFE_DETECTION_EN BIT(11) ++ + #define ANALOG_INTERNAL_OPERATION_MAX_US 20 + #define TXRESERVE_MIN 0 + #define TXRESERVE_MAX 7 +@@ -805,10 +818,7 @@ static void mt798x_phy_common_finetune(s + FIELD_PREP(FFE_UPDATE_GAIN_FORCE_VAL_MASK, 0x4) | + FFE_UPDATE_GAIN_FORCE); + +- /* TrFreeze = 0 (mt7988 default) */ +- __phy_write(phydev, 0x11, 0x0); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9686); ++ __mtk_tr_clr_bits(phydev, 0x2, 0xd, 0x3, TR_FREEZE_MASK); + + __mtk_tr_modify(phydev, 0x2, 0xd, 0x6, + SS_TR_KP100_MASK | SS_TR_KF100_MASK | +@@ -1009,10 +1019,8 @@ static void mt798x_phy_eee(struct phy_de + MTK_PHY_TR_READY_SKIP_AFE_WAKEUP); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* Regsigdet_sel_1000 = 0 */ +- __phy_write(phydev, 0x11, 0xb); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9690); ++ __mtk_tr_clr_bits(phydev, 0x2, 0xd, 0x8, ++ EEE1000_SELECT_SIGNAL_DETECTION_FROM_DFE); + + __mtk_tr_modify(phydev, 0x2, 0xd, 0xd, + EEE1000_STAGE2_TR_KF_MASK, +@@ -1036,10 +1044,8 @@ static void mt798x_phy_eee(struct phy_de + FIELD_PREP(EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK, + 0x10)); + +- /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */ +- __phy_write(phydev, 0x11, 0x1463); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96ca); ++ __mtk_tr_clr_bits(phydev, 0x2, 0xd, 0x25, ++ WAKE_SLAVE_TR_WAIT_DFE_DETECTION_EN); + + __mtk_tr_modify(phydev, 0x1, 0xf, 0x0, + DFE_TAIL_EANBLE_VGA_TRHESH_1000, +--- a/drivers/net/phy/mediatek/mtk-phy-lib.c ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -76,6 +76,13 @@ void __mtk_tr_set_bits(struct phy_device + } + EXPORT_SYMBOL_GPL(__mtk_tr_set_bits); + ++void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 clr) ++{ ++ __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, clr, 0); ++} ++EXPORT_SYMBOL_GPL(__mtk_tr_clr_bits); ++ + int mtk_phy_read_page(struct phy_device *phydev) + { + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -74,6 +74,8 @@ void mtk_tr_modify(struct phy_device *ph + u8 data_addr, u32 mask, u32 set); + void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 set); ++void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 clr); + + int mtk_phy_read_page(struct phy_device *phydev); + int mtk_phy_write_page(struct phy_device *phydev, int page); diff --git a/target/linux/generic/backport-6.12/720-14-v6.15-net-phy-mediatek-Move-some-macros-to-phy-lib-for-lat.patch b/target/linux/generic/backport-6.12/720-14-v6.15-net-phy-mediatek-Move-some-macros-to-phy-lib-for-lat.patch new file mode 100644 index 00000000000..858de093a29 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-14-v6.15-net-phy-mediatek-Move-some-macros-to-phy-lib-for-lat.patch @@ -0,0 +1,45 @@ +From bae8c61522c4d5a5250a24dcb57d120ea593fab1 Mon Sep 17 00:00:00 2001 +From: Sky Huang +Date: Thu, 13 Feb 2025 16:05:53 +0800 +Subject: [PATCH 14/20] net: phy: mediatek: Move some macros to phy-lib for + later use + +Move some macros to phy-lib because MediaTek's 2.5G built-in +ethernet PHY will also use them. + +Signed-off-by: Sky Huang +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250213080553.921434-6-SkyLake.Huang@mediatek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge.c | 4 ---- + drivers/net/phy/mediatek/mtk.h | 4 ++++ + 2 files changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -8,10 +8,6 @@ + #define MTK_GPHY_ID_MT7530 0x03a29412 + #define MTK_GPHY_ID_MT7531 0x03a29441 + +-#define MTK_PHY_PAGE_EXTENDED_1 0x0001 +-#define MTK_PHY_AUX_CTRL_AND_STATUS 0x14 +-#define MTK_PHY_ENABLE_DOWNSHIFT BIT(4) +- + #define MTK_PHY_PAGE_EXTENDED_2 0x0002 + #define MTK_PHY_PAGE_EXTENDED_3 0x0003 + #define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG11 0x11 +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -8,7 +8,11 @@ + #ifndef _MTK_EPHY_H_ + #define _MTK_EPHY_H_ + ++#define MTK_PHY_AUX_CTRL_AND_STATUS 0x14 ++#define MTK_PHY_ENABLE_DOWNSHIFT BIT(4) ++ + #define MTK_EXT_PAGE_ACCESS 0x1f ++#define MTK_PHY_PAGE_EXTENDED_1 0x0001 + #define MTK_PHY_PAGE_STANDARD 0x0000 + #define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + diff --git a/target/linux/generic/backport-6.12/720-15-v6.16-net-phy-mediatek-permit-to-compile-test-GE-SOC-PHY-d.patch b/target/linux/generic/backport-6.12/720-15-v6.16-net-phy-mediatek-permit-to-compile-test-GE-SOC-PHY-d.patch new file mode 100644 index 00000000000..9778ea158b4 --- /dev/null +++ b/target/linux/generic/backport-6.12/720-15-v6.16-net-phy-mediatek-permit-to-compile-test-GE-SOC-PHY-d.patch @@ -0,0 +1,37 @@ +From e5566162af8b9690e096d2e6089e4ed955a0d13d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 10 Apr 2025 12:04:03 +0200 +Subject: [PATCH] net: phy: mediatek: permit to compile test GE SOC PHY driver + +When commit 462a3daad679 ("net: phy: mediatek: fix compile-test +dependencies") fixed the dependency, it should have also introduced +an or on COMPILE_TEST to permit this driver to be compile-tested even if +NVMEM_MTK_EFUSE wasn't selected. The driver makes use of NVMEM API that +are always compiled (return error) so the driver can actually be +compiled even without that config. + +Fix and simplify the dependency condition of this kernel config. + +Fixes: 462a3daad679 ("net: phy: mediatek: fix compile-test dependencies") +Acked-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: Christian Marangi +Acked-by: Arnd Bergmann +Link: https://patch.msgid.link/20250410100410.348-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/Kconfig | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/phy/mediatek/Kconfig ++++ b/drivers/net/phy/mediatek/Kconfig +@@ -15,8 +15,7 @@ config MEDIATEK_GE_PHY + + config MEDIATEK_GE_SOC_PHY + tristate "MediaTek SoC Ethernet PHYs" +- depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST +- depends on NVMEM_MTK_EFUSE ++ depends on (ARM64 && ARCH_MEDIATEK && NVMEM_MTK_EFUSE) || COMPILE_TEST + select MTK_NET_PHYLIB + help + Supports MediaTek SoC built-in Gigabit Ethernet PHYs. diff --git a/target/linux/generic/backport-6.12/720-16-v6.16-net-phy-mediatek-add-Airoha-PHY-ID-to-SoC-driver.patch b/target/linux/generic/backport-6.12/720-16-v6.16-net-phy-mediatek-add-Airoha-PHY-ID-to-SoC-driver.patch new file mode 100644 index 00000000000..5498ecbd21e --- /dev/null +++ b/target/linux/generic/backport-6.12/720-16-v6.16-net-phy-mediatek-add-Airoha-PHY-ID-to-SoC-driver.patch @@ -0,0 +1,129 @@ +From 4590c8bc10951feee3e439bf7fff1b458c2e6fad Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 10 Apr 2025 12:04:04 +0200 +Subject: [PATCH 17/20] net: phy: mediatek: add Airoha PHY ID to SoC driver + +Airoha AN7581 SoC ship with a Switch based on the MT753x Switch embedded +in other SoC like the MT7581 and the MT7988. Similar to these they +require configuring some pin to enable LED PHYs. + +Add support for the PHY ID for the Airoha embedded Switch and define a +simple probe function to toggle these pins. Also fill the LED functions +and add dedicated function to define LED polarity. + +Reviewed-by: Andrew Lunn +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250410100410.348-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/Kconfig | 4 +- + drivers/net/phy/mediatek/mtk-ge-soc.c | 62 +++++++++++++++++++++++++++ + 2 files changed, 65 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/mediatek/Kconfig ++++ b/drivers/net/phy/mediatek/Kconfig +@@ -15,7 +15,9 @@ config MEDIATEK_GE_PHY + + config MEDIATEK_GE_SOC_PHY + tristate "MediaTek SoC Ethernet PHYs" +- depends on (ARM64 && ARCH_MEDIATEK && NVMEM_MTK_EFUSE) || COMPILE_TEST ++ depends on ARM64 || COMPILE_TEST ++ depends on ARCH_AIROHA || (ARCH_MEDIATEK && NVMEM_MTK_EFUSE) || \ ++ COMPILE_TEST + select MTK_NET_PHYLIB + help + Supports MediaTek SoC built-in Gigabit Ethernet PHYs. +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -10,8 +10,11 @@ + + #include "mtk.h" + ++#define MTK_PHY_MAX_LEDS 2 ++ + #define MTK_GPHY_ID_MT7981 0x03a29461 + #define MTK_GPHY_ID_MT7988 0x03a29481 ++#define MTK_GPHY_ID_AN7581 0x03a294c1 + + #define MTK_EXT_PAGE_ACCESS 0x1f + #define MTK_PHY_PAGE_STANDARD 0x0000 +@@ -1405,6 +1408,53 @@ static int mt7981_phy_probe(struct phy_d + return mt798x_phy_calibration(phydev); + } + ++static int an7581_phy_probe(struct phy_device *phydev) ++{ ++ struct mtk_socphy_priv *priv; ++ struct pinctrl *pinctrl; ++ ++ /* Toggle pinctrl to enable PHY LED */ ++ pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led"); ++ if (IS_ERR(pinctrl)) ++ dev_err(&phydev->mdio.bus->dev, ++ "Failed to setup PHY LED pinctrl\n"); ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ phydev->priv = priv; ++ ++ return 0; ++} ++ ++static int an7581_phy_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ u32 mode; ++ u16 val; ++ ++ if (index >= MTK_PHY_MAX_LEDS) ++ return -EINVAL; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ val = MTK_PHY_LED_ON_POLARITY; ++ break; ++ case PHY_LED_ACTIVE_HIGH: ++ val = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED_ON_POLARITY, val); ++} ++ + static struct phy_driver mtk_socphy_driver[] = { + { + PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981), +@@ -1440,6 +1490,17 @@ static struct phy_driver mtk_socphy_driv + .led_hw_control_set = mt798x_phy_led_hw_control_set, + .led_hw_control_get = mt798x_phy_led_hw_control_get, + }, ++ { ++ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_AN7581), ++ .name = "Airoha AN7581 PHY", ++ .probe = an7581_phy_probe, ++ .led_blink_set = mt798x_phy_led_blink_set, ++ .led_brightness_set = mt798x_phy_led_brightness_set, ++ .led_hw_is_supported = mt798x_phy_led_hw_is_supported, ++ .led_hw_control_set = mt798x_phy_led_hw_control_set, ++ .led_hw_control_get = mt798x_phy_led_hw_control_get, ++ .led_polarity_set = an7581_phy_led_polarity_set, ++ }, + }; + + module_phy_driver(mtk_socphy_driver); +@@ -1447,6 +1508,7 @@ module_phy_driver(mtk_socphy_driver); + static const struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = { + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981) }, + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988) }, ++ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_AN7581) }, + { } + }; + diff --git a/target/linux/generic/backport-6.12/720-17-v6.16-net-phy-mediatek-init-val-in-.phy_led_polarity_set-f.patch b/target/linux/generic/backport-6.12/720-17-v6.16-net-phy-mediatek-init-val-in-.phy_led_polarity_set-f.patch new file mode 100644 index 00000000000..1b1dda2327b --- /dev/null +++ b/target/linux/generic/backport-6.12/720-17-v6.16-net-phy-mediatek-init-val-in-.phy_led_polarity_set-f.patch @@ -0,0 +1,40 @@ +From 34501d047ac0a6cbb13285ba9d15f75c1deb7da7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 15 Apr 2025 12:53:05 +0200 +Subject: [PATCH 18/20] net: phy: mediatek: init val in .phy_led_polarity_set + for AN7581 + +Fix smatch warning for uninitialised val in .phy_led_polarity_set for +AN7581 driver. + +Correctly init to 0 to set polarity high by default. + +Reported-by: Simon Horman +Fixes: 6a325aed130b ("net: phy: mediatek: add Airoha PHY ID to SoC driver") +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250415105313.3409-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge-soc.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -1431,8 +1431,8 @@ static int an7581_phy_probe(struct phy_d + static int an7581_phy_led_polarity_set(struct phy_device *phydev, int index, + unsigned long modes) + { ++ u16 val = 0; + u32 mode; +- u16 val; + + if (index >= MTK_PHY_MAX_LEDS) + return -EINVAL; +@@ -1443,7 +1443,6 @@ static int an7581_phy_led_polarity_set(s + val = MTK_PHY_LED_ON_POLARITY; + break; + case PHY_LED_ACTIVE_HIGH: +- val = 0; + break; + default: + return -EINVAL; diff --git a/target/linux/generic/backport-6.12/721-01-v6.15-net-ethernet-mediatek-add-EEE-support.patch b/target/linux/generic/backport-6.12/721-01-v6.15-net-ethernet-mediatek-add-EEE-support.patch new file mode 100644 index 00000000000..7a94897c02f --- /dev/null +++ b/target/linux/generic/backport-6.12/721-01-v6.15-net-ethernet-mediatek-add-EEE-support.patch @@ -0,0 +1,157 @@ +From 952d7325362ffbefa6ce5619fb4e53c2159ec7a7 Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Mon, 17 Feb 2025 17:40:21 +0800 +Subject: [PATCH] net: ethernet: mediatek: add EEE support + +Add EEE support to MediaTek SoC Ethernet. The register fields are +similar to the ones in MT7531, except that the LPI threshold is in +milliseconds. + +Signed-off-by: Qingfang Deng +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 64 +++++++++++++++++++++ + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 11 ++++ + 2 files changed, 75 insertions(+) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -782,6 +782,7 @@ static void mtk_mac_link_up(struct phyli + + mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | ++ MAC_MCR_EEE100M | MAC_MCR_EEE1G | + MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | + MAC_MCR_FORCE_RX_FC); + +@@ -807,6 +808,15 @@ static void mtk_mac_link_up(struct phyli + if (rx_pause) + mcr |= MAC_MCR_FORCE_RX_FC; + ++ if (mode == MLO_AN_PHY && phy && mac->tx_lpi_enabled && phy_init_eee(phy, false) >= 0) { ++ mcr |= MAC_MCR_EEE100M | MAC_MCR_EEE1G; ++ mtk_w32(mac->hw, ++ FIELD_PREP(MAC_EEE_WAKEUP_TIME_1000, 17) | ++ FIELD_PREP(MAC_EEE_WAKEUP_TIME_100, 36) | ++ FIELD_PREP(MAC_EEE_LPI_TXIDLE_THD, mac->txidle_thd_ms), ++ MTK_MAC_EEECR(mac->id)); ++ } ++ + mcr |= MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK; + mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); + } +@@ -4523,6 +4533,61 @@ static int mtk_set_pauseparam(struct net + return phylink_ethtool_set_pauseparam(mac->phylink, pause); + } + ++static int mtk_get_eee(struct net_device *dev, struct ethtool_keee *eee) ++{ ++ struct mtk_mac *mac = netdev_priv(dev); ++ u32 reg; ++ int ret; ++ ++ ret = phylink_ethtool_get_eee(mac->phylink, eee); ++ if (ret) ++ return ret; ++ ++ reg = mtk_r32(mac->hw, MTK_MAC_EEECR(mac->id)); ++ eee->tx_lpi_enabled = mac->tx_lpi_enabled; ++ eee->tx_lpi_timer = FIELD_GET(MAC_EEE_LPI_TXIDLE_THD, reg) * 1000; ++ ++ return 0; ++} ++ ++static int mtk_set_eee(struct net_device *dev, struct ethtool_keee *eee) ++{ ++ struct mtk_mac *mac = netdev_priv(dev); ++ u32 txidle_thd_ms, reg; ++ int ret; ++ ++ /* Tx idle timer in ms */ ++ txidle_thd_ms = DIV_ROUND_UP(eee->tx_lpi_timer, 1000); ++ if (!FIELD_FIT(MAC_EEE_LPI_TXIDLE_THD, txidle_thd_ms)) ++ return -EINVAL; ++ ++ reg = FIELD_PREP(MAC_EEE_LPI_TXIDLE_THD, txidle_thd_ms); ++ ++ /* PHY Wake-up time, this field does not have a reset value, so use the ++ * reset value from MT7531 (36us for 100BaseT and 17us for 1000BaseT). ++ */ ++ reg |= FIELD_PREP(MAC_EEE_WAKEUP_TIME_1000, 17) | ++ FIELD_PREP(MAC_EEE_WAKEUP_TIME_100, 36); ++ ++ if (!txidle_thd_ms) ++ /* Force LPI Mode without a delay */ ++ reg |= MAC_EEE_LPI_MODE; ++ ++ ret = phylink_ethtool_set_eee(mac->phylink, eee); ++ if (ret) ++ return ret; ++ ++ mac->tx_lpi_enabled = eee->tx_lpi_enabled; ++ mac->txidle_thd_ms = txidle_thd_ms; ++ mtk_w32(mac->hw, reg, MTK_MAC_EEECR(mac->id)); ++ if (eee->eee_enabled && eee->eee_active && eee->tx_lpi_enabled) ++ mtk_m32(mac->hw, 0, MAC_MCR_EEE100M | MAC_MCR_EEE1G, MTK_MAC_MCR(mac->id)); ++ else ++ mtk_m32(mac->hw, MAC_MCR_EEE100M | MAC_MCR_EEE1G, 0, MTK_MAC_MCR(mac->id)); ++ ++ return 0; ++} ++ + static u16 mtk_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) + { +@@ -4555,6 +4620,8 @@ static const struct ethtool_ops mtk_etht + .set_pauseparam = mtk_set_pauseparam, + .get_rxnfc = mtk_get_rxnfc, + .set_rxnfc = mtk_set_rxnfc, ++ .get_eee = mtk_get_eee, ++ .set_eee = mtk_set_eee, + }; + + static const struct net_device_ops mtk_netdev_ops = { +@@ -4615,6 +4682,8 @@ static int mtk_add_mac(struct mtk_eth *e + } + mac = netdev_priv(eth->netdev[id]); + eth->mac[id] = mac; ++ mac->tx_lpi_enabled = true; ++ mac->txidle_thd_ms = 1; + mac->id = id; + mac->hw = eth; + mac->of_node = np; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -461,6 +461,8 @@ + #define MAC_MCR_RX_FIFO_CLR_DIS BIT(12) + #define MAC_MCR_BACKOFF_EN BIT(9) + #define MAC_MCR_BACKPR_EN BIT(8) ++#define MAC_MCR_EEE1G BIT(7) ++#define MAC_MCR_EEE100M BIT(6) + #define MAC_MCR_FORCE_RX_FC BIT(5) + #define MAC_MCR_FORCE_TX_FC BIT(4) + #define MAC_MCR_SPEED_1000 BIT(3) +@@ -469,6 +471,15 @@ + #define MAC_MCR_FORCE_LINK BIT(0) + #define MAC_MCR_FORCE_LINK_DOWN (MAC_MCR_FORCE_MODE) + ++/* Mac EEE control registers */ ++#define MTK_MAC_EEECR(x) (0x10104 + (x * 0x100)) ++#define MAC_EEE_WAKEUP_TIME_1000 GENMASK(31, 24) ++#define MAC_EEE_WAKEUP_TIME_100 GENMASK(23, 16) ++#define MAC_EEE_LPI_TXIDLE_THD GENMASK(15, 8) ++#define MAC_EEE_CKG_TXIDLE BIT(3) ++#define MAC_EEE_CKG_RXLPI BIT(2) ++#define MAC_EEE_LPI_MODE BIT(0) ++ + /* Mac status registers */ + #define MTK_MAC_MSR(x) (0x10108 + (x * 0x100)) + #define MAC_MSR_EEE1G BIT(7) +@@ -1316,6 +1327,8 @@ struct mtk_mac { + int id; + phy_interface_t interface; + u8 ppe_idx; ++ bool tx_lpi_enabled; ++ u8 txidle_thd_ms; + int speed; + struct device_node *of_node; + struct phylink *phylink; diff --git a/target/linux/generic/backport-6.12/730-01-v6.13-net-phy-aquantia-allow-forcing-order-of-MDI-pairs.patch b/target/linux/generic/backport-6.12/730-01-v6.13-net-phy-aquantia-allow-forcing-order-of-MDI-pairs.patch new file mode 100644 index 00000000000..aabaa33e2cc --- /dev/null +++ b/target/linux/generic/backport-6.12/730-01-v6.13-net-phy-aquantia-allow-forcing-order-of-MDI-pairs.patch @@ -0,0 +1,107 @@ +From a2e1ba275eae96a8171deb19e9c7c2f5978fee7b Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 4 Oct 2024 17:18:16 +0100 +Subject: [PATCH] net: phy: aquantia: allow forcing order of MDI pairs + +Despite supporting Auto MDI-X, it looks like Aquantia only supports +swapping pair (1,2) with pair (3,6) like it used to be for MDI-X on +100MBit/s networks. + +When all 4 pairs are in use (for 1000MBit/s or faster) the link does not +come up with pair order is not configured correctly, either using +MDI_CFG pin or using the "PMA Receive Reserved Vendor Provisioning 1" +register. + +Normally, the order of MDI pairs being either ABCD or DCBA is configured +by pulling the MDI_CFG pin. + +However, some hardware designs require overriding the value configured +by that bootstrap pin. The PHY allows doing that by setting a bit in +"PMA Receive Reserved Vendor Provisioning 1" register which allows +ignoring the state of the MDI_CFG pin and another bit configuring +whether the order of MDI pairs should be normal (ABCD) or reverse +(DCBA). Pair polarity is not affected and remains identical in both +settings. + +Introduce property "marvell,mdi-cfg-order" which allows forcing either +normal or reverse order of the MDI pairs from DT. + +If the property isn't present, the behavior is unchanged and MDI pair +order configuration is untouched (ie. either the result of MDI_CFG pin +pull-up/pull-down, or pair order override already configured by the +bootloader before Linux is started). + +Forcing normal pair order is required on the Adtran SDG-8733A Wi-Fi 7 +residential gateway. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/9ed760ff87d5fc456f31e407ead548bbb754497d.1728058550.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/aquantia/aquantia_main.c | 33 ++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + + #include "aquantia.h" +@@ -71,6 +72,11 @@ + #define MDIO_AN_TX_VEND_INT_MASK2 0xd401 + #define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0) + ++#define PMAPMD_RSVD_VEND_PROV 0xe400 ++#define PMAPMD_RSVD_VEND_PROV_MDI_CONF GENMASK(1, 0) ++#define PMAPMD_RSVD_VEND_PROV_MDI_REVERSE BIT(0) ++#define PMAPMD_RSVD_VEND_PROV_MDI_FORCE BIT(1) ++ + #define MDIO_AN_RX_LP_STAT1 0xe820 + #define MDIO_AN_RX_LP_STAT1_1000BASET_FULL BIT(15) + #define MDIO_AN_RX_LP_STAT1_1000BASET_HALF BIT(14) +@@ -485,6 +491,29 @@ static void aqr107_chip_info(struct phy_ + fw_major, fw_minor, build_id, prov_id); + } + ++static int aqr107_config_mdi(struct phy_device *phydev) ++{ ++ struct device_node *np = phydev->mdio.dev.of_node; ++ u32 mdi_conf; ++ int ret; ++ ++ ret = of_property_read_u32(np, "marvell,mdi-cfg-order", &mdi_conf); ++ ++ /* Do nothing in case property "marvell,mdi-cfg-order" is not present */ ++ if (ret == -ENOENT) ++ return 0; ++ ++ if (ret) ++ return ret; ++ ++ if (mdi_conf & ~PMAPMD_RSVD_VEND_PROV_MDI_REVERSE) ++ return -EINVAL; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_RSVD_VEND_PROV, ++ PMAPMD_RSVD_VEND_PROV_MDI_CONF, ++ mdi_conf | PMAPMD_RSVD_VEND_PROV_MDI_FORCE); ++} ++ + static int aqr107_config_init(struct phy_device *phydev) + { + struct aqr107_priv *priv = phydev->priv; +@@ -514,6 +543,10 @@ static int aqr107_config_init(struct phy + if (ret) + return ret; + ++ ret = aqr107_config_mdi(phydev); ++ if (ret) ++ return ret; ++ + /* Restore LED polarity state after reset */ + for_each_set_bit(led_active_low, &priv->leds_active_low, AQR_MAX_LEDS) { + ret = aqr_phy_led_active_low_set(phydev, led_active_low, true); diff --git a/target/linux/generic/backport-6.12/730-02-v6.13-net-phy-aquantia-fix-return-value-check-in-aqr107_co.patch b/target/linux/generic/backport-6.12/730-02-v6.13-net-phy-aquantia-fix-return-value-check-in-aqr107_co.patch new file mode 100644 index 00000000000..565edbd388d --- /dev/null +++ b/target/linux/generic/backport-6.12/730-02-v6.13-net-phy-aquantia-fix-return-value-check-in-aqr107_co.patch @@ -0,0 +1,31 @@ +From ce21b8fb255ebf0b49913fb4c62741d7eb05c6f6 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 11 Oct 2024 22:28:43 +0100 +Subject: [PATCH] net: phy: aquantia: fix return value check in + aqr107_config_mdi() + +of_property_read_u32() returns -EINVAL in case the property cannot be +found rather than -ENOENT. Fix the check to not abort probing in case +of the property being missing, and also in case CONFIG_OF is not set +which will result in -ENOSYS. + +Fixes: a2e1ba275eae ("net: phy: aquantia: allow forcing order of MDI pairs") +Reported-by: Jon Hunter +Closes: https://lore.kernel.org/all/114b4c03-5d16-42ed-945d-cf78eabea12b@nvidia.com/ +Suggested-by: Hans-Frieder Vogt +Signed-off-by: Daniel Golle +--- + drivers/net/phy/aquantia/aquantia_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -500,7 +500,7 @@ static int aqr107_config_mdi(struct phy_ + ret = of_property_read_u32(np, "marvell,mdi-cfg-order", &mdi_conf); + + /* Do nothing in case property "marvell,mdi-cfg-order" is not present */ +- if (ret == -ENOENT) ++ if (ret == -EINVAL || ret == -ENOSYS) + return 0; + + if (ret) diff --git a/target/linux/generic/backport-6.12/730-03-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch b/target/linux/generic/backport-6.12/730-03-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch new file mode 100644 index 00000000000..6822dc0f227 --- /dev/null +++ b/target/linux/generic/backport-6.12/730-03-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch @@ -0,0 +1,53 @@ +From a274465cc3bef2dfd9c9ea5100848dda0a8641e1 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 10 Oct 2024 13:54:19 +0100 +Subject: [PATCH 1/4] net: phy: support 'active-high' property for PHY LEDs + +In addition to 'active-low' and 'inactive-high-impedance' also +support 'active-high' property for PHY LED pin configuration. +As only either 'active-high' or 'active-low' can be set at the +same time, WARN and return an error in case both are set. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/91598487773d768f254d5faf06cf65b13e972f0e.1728558223.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/phy_device.c | 6 ++++++ + include/linux/phy.h | 5 +++-- + 2 files changed, 9 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -3387,11 +3387,17 @@ static int of_phy_led(struct phy_device + if (index > U8_MAX) + return -EINVAL; + ++ if (of_property_read_bool(led, "active-high")) ++ set_bit(PHY_LED_ACTIVE_HIGH, &modes); + if (of_property_read_bool(led, "active-low")) + set_bit(PHY_LED_ACTIVE_LOW, &modes); + if (of_property_read_bool(led, "inactive-high-impedance")) + set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes); + ++ if (WARN_ON(modes & BIT(PHY_LED_ACTIVE_LOW) && ++ modes & BIT(PHY_LED_ACTIVE_HIGH))) ++ return -EINVAL; ++ + if (modes) { + /* Return error if asked to set polarity modes but not supported */ + if (!phydev->drv->led_polarity_set) +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -902,8 +902,9 @@ struct phy_plca_status { + + /* Modes for PHY LED configuration */ + enum phy_led_modes { +- PHY_LED_ACTIVE_LOW = 0, +- PHY_LED_INACTIVE_HIGH_IMPEDANCE = 1, ++ PHY_LED_ACTIVE_HIGH = 0, ++ PHY_LED_ACTIVE_LOW = 1, ++ PHY_LED_INACTIVE_HIGH_IMPEDANCE = 2, + + /* keep it last */ + __PHY_LED_MODES_NUM, diff --git a/target/linux/generic/backport-6.12/730-04-v6.13-net-phy-aquantia-correctly-describe-LED-polarity-ove.patch b/target/linux/generic/backport-6.12/730-04-v6.13-net-phy-aquantia-correctly-describe-LED-polarity-ove.patch new file mode 100644 index 00000000000..155f796f8c0 --- /dev/null +++ b/target/linux/generic/backport-6.12/730-04-v6.13-net-phy-aquantia-correctly-describe-LED-polarity-ove.patch @@ -0,0 +1,108 @@ +From 9d55e68b19f222e6334ef4021c5527998f5ab537 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 10 Oct 2024 13:55:00 +0100 +Subject: [PATCH 2/4] net: phy: aquantia: correctly describe LED polarity + override + +Use newly defined 'active-high' property to set the +VEND1_GLOBAL_LED_DRIVE_VDD bit and let 'active-low' clear that bit. This +reflects the technical reality which was inverted in the previous +description in which the 'active-low' property was used to actually set +the VEND1_GLOBAL_LED_DRIVE_VDD bit, which means that VDD (ie. supply +voltage) of the LED is driven rather than GND. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/86a413b4387c42dcb54f587cc2433a06f16aae83.1728558223.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/aquantia/aquantia.h | 1 + + drivers/net/phy/aquantia/aquantia_leds.c | 19 ++++++++++++++----- + drivers/net/phy/aquantia/aquantia_main.c | 12 +++++++++--- + 3 files changed, 24 insertions(+), 8 deletions(-) + +--- a/drivers/net/phy/aquantia/aquantia.h ++++ b/drivers/net/phy/aquantia/aquantia.h +@@ -177,6 +177,7 @@ static const struct aqr107_hw_stat aqr10 + struct aqr107_priv { + u64 sgmii_stats[AQR107_SGMII_STAT_SZ]; + unsigned long leds_active_low; ++ unsigned long leds_active_high; + }; + + #if IS_REACHABLE(CONFIG_HWMON) +--- a/drivers/net/phy/aquantia/aquantia_leds.c ++++ b/drivers/net/phy/aquantia/aquantia_leds.c +@@ -121,13 +121,13 @@ int aqr_phy_led_active_low_set(struct ph + { + return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_DRIVE(index), + VEND1_GLOBAL_LED_DRIVE_VDD, +- enable ? VEND1_GLOBAL_LED_DRIVE_VDD : 0); ++ enable ? 0 : VEND1_GLOBAL_LED_DRIVE_VDD); + } + + int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes) + { ++ bool force_active_low = false, force_active_high = false; + struct aqr107_priv *priv = phydev->priv; +- bool active_low = false; + u32 mode; + + if (index >= AQR_MAX_LEDS) +@@ -136,7 +136,10 @@ int aqr_phy_led_polarity_set(struct phy_ + for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { + switch (mode) { + case PHY_LED_ACTIVE_LOW: +- active_low = true; ++ force_active_low = true; ++ break; ++ case PHY_LED_ACTIVE_HIGH: ++ force_active_high = true; + break; + default: + return -EINVAL; +@@ -144,8 +147,14 @@ int aqr_phy_led_polarity_set(struct phy_ + } + + /* Save LED driver vdd state to restore on SW reset */ +- if (active_low) ++ if (force_active_low) + priv->leds_active_low |= BIT(index); + +- return aqr_phy_led_active_low_set(phydev, index, active_low); ++ if (force_active_high) ++ priv->leds_active_high |= BIT(index); ++ ++ if (force_active_high || force_active_low) ++ return aqr_phy_led_active_low_set(phydev, index, force_active_low); ++ ++ unreachable(); + } +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -517,7 +517,7 @@ static int aqr107_config_mdi(struct phy_ + static int aqr107_config_init(struct phy_device *phydev) + { + struct aqr107_priv *priv = phydev->priv; +- u32 led_active_low; ++ u32 led_idx; + int ret; + + /* Check that the PHY interface type is compatible */ +@@ -548,8 +548,14 @@ static int aqr107_config_init(struct phy + return ret; + + /* Restore LED polarity state after reset */ +- for_each_set_bit(led_active_low, &priv->leds_active_low, AQR_MAX_LEDS) { +- ret = aqr_phy_led_active_low_set(phydev, led_active_low, true); ++ for_each_set_bit(led_idx, &priv->leds_active_low, AQR_MAX_LEDS) { ++ ret = aqr_phy_led_active_low_set(phydev, led_idx, true); ++ if (ret) ++ return ret; ++ } ++ ++ for_each_set_bit(led_idx, &priv->leds_active_high, AQR_MAX_LEDS) { ++ ret = aqr_phy_led_active_low_set(phydev, led_idx, false); + if (ret) + return ret; + } diff --git a/target/linux/generic/backport-6.12/730-05-v6.13-net-phy-mxl-gpy-add-basic-LED-support.patch b/target/linux/generic/backport-6.12/730-05-v6.13-net-phy-mxl-gpy-add-basic-LED-support.patch new file mode 100644 index 00000000000..c785f8a98a8 --- /dev/null +++ b/target/linux/generic/backport-6.12/730-05-v6.13-net-phy-mxl-gpy-add-basic-LED-support.patch @@ -0,0 +1,332 @@ +From 78997e9a5e4d8a4df561e083a92c91ae23010e07 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 1 Oct 2024 01:17:18 +0100 +Subject: [PATCH] net: phy: mxl-gpy: add basic LED support + +Add basic support for LEDs connected to MaxLinear GPY2xx and GPY115 PHYs. +The PHYs allow up to 4 LEDs to be connected. +Implement controlling LEDs in software as well as netdev trigger offloading +and LED polarity setup. + +The hardware claims to support 16 PWM brightness levels but there is no +documentation on how to use that feature, hence this is not supported. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/b6ec9050339f8244ff898898a1cecc33b13a48fc.1727741563.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mxl-gpy.c | 218 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 218 insertions(+) + +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -38,6 +38,7 @@ + #define PHY_MIISTAT 0x18 /* MII state */ + #define PHY_IMASK 0x19 /* interrupt mask */ + #define PHY_ISTAT 0x1A /* interrupt status */ ++#define PHY_LED 0x1B /* LEDs */ + #define PHY_FWV 0x1E /* firmware version */ + + #define PHY_MIISTAT_SPD_MASK GENMASK(2, 0) +@@ -61,6 +62,11 @@ + PHY_IMASK_ADSC | \ + PHY_IMASK_ANC) + ++#define GPY_MAX_LEDS 4 ++#define PHY_LED_POLARITY(idx) BIT(12 + (idx)) ++#define PHY_LED_HWCONTROL(idx) BIT(8 + (idx)) ++#define PHY_LED_ON(idx) BIT(idx) ++ + #define PHY_FWV_REL_MASK BIT(15) + #define PHY_FWV_MAJOR_MASK GENMASK(11, 8) + #define PHY_FWV_MINOR_MASK GENMASK(7, 0) +@@ -72,6 +78,23 @@ + #define PHY_MDI_MDI_X_CD 0x1 + #define PHY_MDI_MDI_X_CROSS 0x0 + ++/* LED */ ++#define VSPEC1_LED(idx) (1 + (idx)) ++#define VSPEC1_LED_BLINKS GENMASK(15, 12) ++#define VSPEC1_LED_PULSE GENMASK(11, 8) ++#define VSPEC1_LED_CON GENMASK(7, 4) ++#define VSPEC1_LED_BLINKF GENMASK(3, 0) ++ ++#define VSPEC1_LED_LINK10 BIT(0) ++#define VSPEC1_LED_LINK100 BIT(1) ++#define VSPEC1_LED_LINK1000 BIT(2) ++#define VSPEC1_LED_LINK2500 BIT(3) ++ ++#define VSPEC1_LED_TXACT BIT(0) ++#define VSPEC1_LED_RXACT BIT(1) ++#define VSPEC1_LED_COL BIT(2) ++#define VSPEC1_LED_NO_CON BIT(3) ++ + /* SGMII */ + #define VSPEC1_SGMII_CTRL 0x08 + #define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */ +@@ -835,6 +858,156 @@ static int gpy115_loopback(struct phy_de + return genphy_soft_reset(phydev); + } + ++static int gpy_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ int ret; ++ ++ if (index >= GPY_MAX_LEDS) ++ return -EINVAL; ++ ++ /* clear HWCONTROL and set manual LED state */ ++ ret = phy_modify(phydev, PHY_LED, ++ ((value == LED_OFF) ? PHY_LED_HWCONTROL(index) : 0) | ++ PHY_LED_ON(index), ++ (value == LED_OFF) ? 0 : PHY_LED_ON(index)); ++ if (ret) ++ return ret; ++ ++ /* ToDo: set PWM brightness */ ++ ++ /* clear HW LED setup */ ++ if (value == LED_OFF) ++ return phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index), 0); ++ else ++ return 0; ++} ++ ++static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX)); ++ ++static int gpy_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ if (index >= GPY_MAX_LEDS) ++ return -EINVAL; ++ ++ /* All combinations of the supported triggers are allowed */ ++ if (rules & ~supported_triggers) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++ ++static int gpy_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ int val; ++ ++ if (index >= GPY_MAX_LEDS) ++ return -EINVAL; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index)); ++ if (val < 0) ++ return val; ++ ++ if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK10) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_10); ++ ++ if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK100) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_100); ++ ++ if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK1000) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_1000); ++ ++ if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK2500) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_2500); ++ ++ if (FIELD_GET(VSPEC1_LED_CON, val) == (VSPEC1_LED_LINK10 | ++ VSPEC1_LED_LINK100 | ++ VSPEC1_LED_LINK1000 | ++ VSPEC1_LED_LINK2500)) ++ *rules |= BIT(TRIGGER_NETDEV_LINK); ++ ++ if (FIELD_GET(VSPEC1_LED_PULSE, val) & VSPEC1_LED_TXACT) ++ *rules |= BIT(TRIGGER_NETDEV_TX); ++ ++ if (FIELD_GET(VSPEC1_LED_PULSE, val) & VSPEC1_LED_RXACT) ++ *rules |= BIT(TRIGGER_NETDEV_RX); ++ ++ return 0; ++} ++ ++static int gpy_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 val = 0; ++ int ret; ++ ++ if (index >= GPY_MAX_LEDS) ++ return -EINVAL; ++ ++ if (rules & BIT(TRIGGER_NETDEV_LINK) || ++ rules & BIT(TRIGGER_NETDEV_LINK_10)) ++ val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK10); ++ ++ if (rules & BIT(TRIGGER_NETDEV_LINK) || ++ rules & BIT(TRIGGER_NETDEV_LINK_100)) ++ val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK100); ++ ++ if (rules & BIT(TRIGGER_NETDEV_LINK) || ++ rules & BIT(TRIGGER_NETDEV_LINK_1000)) ++ val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK1000); ++ ++ if (rules & BIT(TRIGGER_NETDEV_LINK) || ++ rules & BIT(TRIGGER_NETDEV_LINK_2500)) ++ val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK2500); ++ ++ if (rules & BIT(TRIGGER_NETDEV_TX)) ++ val |= FIELD_PREP(VSPEC1_LED_PULSE, VSPEC1_LED_TXACT); ++ ++ if (rules & BIT(TRIGGER_NETDEV_RX)) ++ val |= FIELD_PREP(VSPEC1_LED_PULSE, VSPEC1_LED_RXACT); ++ ++ /* allow RX/TX pulse without link indication */ ++ if ((rules & BIT(TRIGGER_NETDEV_TX) || rules & BIT(TRIGGER_NETDEV_RX)) && ++ !(val & VSPEC1_LED_CON)) ++ val |= FIELD_PREP(VSPEC1_LED_PULSE, VSPEC1_LED_NO_CON) | VSPEC1_LED_CON; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index), val); ++ if (ret) ++ return ret; ++ ++ return phy_set_bits(phydev, PHY_LED, PHY_LED_HWCONTROL(index)); ++} ++ ++static int gpy_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ bool active_low = false; ++ u32 mode; ++ ++ if (index >= GPY_MAX_LEDS) ++ return -EINVAL; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ active_low = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return phy_modify(phydev, PHY_LED, PHY_LED_POLARITY(index), ++ active_low ? 0 : PHY_LED_POLARITY(index)); ++} ++ + static struct phy_driver gpy_drivers[] = { + { + PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx), +@@ -852,6 +1025,11 @@ static struct phy_driver gpy_drivers[] = + .set_wol = gpy_set_wol, + .get_wol = gpy_get_wol, + .set_loopback = gpy_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, + }, + { + .phy_id = PHY_ID_GPY115B, +@@ -870,6 +1048,11 @@ static struct phy_driver gpy_drivers[] = + .set_wol = gpy_set_wol, + .get_wol = gpy_get_wol, + .set_loopback = gpy115_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_GPY115C), +@@ -887,6 +1070,11 @@ static struct phy_driver gpy_drivers[] = + .set_wol = gpy_set_wol, + .get_wol = gpy_get_wol, + .set_loopback = gpy115_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, + }, + { + .phy_id = PHY_ID_GPY211B, +@@ -905,6 +1093,11 @@ static struct phy_driver gpy_drivers[] = + .set_wol = gpy_set_wol, + .get_wol = gpy_get_wol, + .set_loopback = gpy_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_GPY211C), +@@ -922,6 +1115,11 @@ static struct phy_driver gpy_drivers[] = + .set_wol = gpy_set_wol, + .get_wol = gpy_get_wol, + .set_loopback = gpy_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, + }, + { + .phy_id = PHY_ID_GPY212B, +@@ -940,6 +1138,11 @@ static struct phy_driver gpy_drivers[] = + .set_wol = gpy_set_wol, + .get_wol = gpy_get_wol, + .set_loopback = gpy_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_GPY212C), +@@ -957,6 +1160,11 @@ static struct phy_driver gpy_drivers[] = + .set_wol = gpy_set_wol, + .get_wol = gpy_get_wol, + .set_loopback = gpy_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, + }, + { + .phy_id = PHY_ID_GPY215B, +@@ -975,6 +1183,11 @@ static struct phy_driver gpy_drivers[] = + .set_wol = gpy_set_wol, + .get_wol = gpy_get_wol, + .set_loopback = gpy_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_GPY215C), +@@ -992,6 +1205,11 @@ static struct phy_driver gpy_drivers[] = + .set_wol = gpy_set_wol, + .get_wol = gpy_get_wol, + .set_loopback = gpy_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_GPY241B), diff --git a/target/linux/generic/backport-6.12/730-06-v6.13-net-phy-mxl-gpy-add-missing-support-for-TRIGGER_NETD.patch b/target/linux/generic/backport-6.12/730-06-v6.13-net-phy-mxl-gpy-add-missing-support-for-TRIGGER_NETD.patch new file mode 100644 index 00000000000..39bef9b9822 --- /dev/null +++ b/target/linux/generic/backport-6.12/730-06-v6.13-net-phy-mxl-gpy-add-missing-support-for-TRIGGER_NETD.patch @@ -0,0 +1,28 @@ +From f95b4725e796b12e5f347a0d161e1d3843142aa8 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 4 Oct 2024 16:56:35 +0100 +Subject: [PATCH] net: phy: mxl-gpy: add missing support for + TRIGGER_NETDEV_LINK_10 + +The PHY also support 10MBit/s links as well as the corresponding link +indication trigger to be offloaded. Add TRIGGER_NETDEV_LINK_10 to the +supported triggers. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/cc5da0a989af8b0d49d823656d88053c4de2ab98.1728057367.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mxl-gpy.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -884,6 +884,7 @@ static int gpy_led_brightness_set(struct + } + + static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | + BIT(TRIGGER_NETDEV_LINK_100) | + BIT(TRIGGER_NETDEV_LINK_1000) | + BIT(TRIGGER_NETDEV_LINK_2500) | diff --git a/target/linux/generic/backport-6.12/730-07-v6.13-net-phy-mxl-gpy-correctly-describe-LED-polarity.patch b/target/linux/generic/backport-6.12/730-07-v6.13-net-phy-mxl-gpy-correctly-describe-LED-polarity.patch new file mode 100644 index 00000000000..5fd3dcc77be --- /dev/null +++ b/target/linux/generic/backport-6.12/730-07-v6.13-net-phy-mxl-gpy-correctly-describe-LED-polarity.patch @@ -0,0 +1,58 @@ +From eb89c79c1b8f17fc1611540768678e60df89ac42 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 10 Oct 2024 13:55:17 +0100 +Subject: [PATCH 3/4] net: phy: mxl-gpy: correctly describe LED polarity + +According the datasheet covering the LED (0x1b) register: +0B Active High LEDx pin driven high when activated +1B Active Low LEDx pin driven low when activated + +Make use of the now available 'active-high' property and correctly +reflect the polarity setting which was previously inverted. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/180ccafa837f09908b852a8a874a3808c5ecd2d0.1728558223.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/mxl-gpy.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -989,7 +989,7 @@ static int gpy_led_hw_control_set(struct + static int gpy_led_polarity_set(struct phy_device *phydev, int index, + unsigned long modes) + { +- bool active_low = false; ++ bool force_active_low = false, force_active_high = false; + u32 mode; + + if (index >= GPY_MAX_LEDS) +@@ -998,15 +998,23 @@ static int gpy_led_polarity_set(struct p + for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { + switch (mode) { + case PHY_LED_ACTIVE_LOW: +- active_low = true; ++ force_active_low = true; ++ break; ++ case PHY_LED_ACTIVE_HIGH: ++ force_active_high = true; + break; + default: + return -EINVAL; + } + } + +- return phy_modify(phydev, PHY_LED, PHY_LED_POLARITY(index), +- active_low ? 0 : PHY_LED_POLARITY(index)); ++ if (force_active_low) ++ return phy_set_bits(phydev, PHY_LED, PHY_LED_POLARITY(index)); ++ ++ if (force_active_high) ++ return phy_clear_bits(phydev, PHY_LED, PHY_LED_POLARITY(index)); ++ ++ unreachable(); + } + + static struct phy_driver gpy_drivers[] = { diff --git a/target/linux/generic/backport-6.12/730-08-v6.13-net-phy-intel-xway-add-support-for-PHY-LEDs.patch b/target/linux/generic/backport-6.12/730-08-v6.13-net-phy-intel-xway-add-support-for-PHY-LEDs.patch new file mode 100644 index 00000000000..c57b5777ad5 --- /dev/null +++ b/target/linux/generic/backport-6.12/730-08-v6.13-net-phy-intel-xway-add-support-for-PHY-LEDs.patch @@ -0,0 +1,379 @@ +From 1758af47b98c17da464cb45f476875150955dd48 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 10 Oct 2024 13:55:29 +0100 +Subject: [PATCH 4/4] net: phy: intel-xway: add support for PHY LEDs + +The intel-xway PHY driver predates the PHY LED framework and currently +initializes all LED pins to equal default values. + +Add PHY LED functions to the drivers and don't set default values if +LEDs are defined in device tree. + +According the datasheets 3 LEDs are supported on all Intel XWAY PHYs. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/81f4717ab9acf38f3239727a4540ae96fd01109b.1728558223.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/intel-xway.c | 253 +++++++++++++++++++++++++++++++++-- + 1 file changed, 244 insertions(+), 9 deletions(-) + +--- a/drivers/net/phy/intel-xway.c ++++ b/drivers/net/phy/intel-xway.c +@@ -151,6 +151,13 @@ + #define XWAY_MMD_LED3H 0x01E8 + #define XWAY_MMD_LED3L 0x01E9 + ++#define XWAY_GPHY_MAX_LEDS 3 ++#define XWAY_GPHY_LED_INV(idx) BIT(12 + (idx)) ++#define XWAY_GPHY_LED_EN(idx) BIT(8 + (idx)) ++#define XWAY_GPHY_LED_DA(idx) BIT(idx) ++#define XWAY_MMD_LEDxH(idx) (XWAY_MMD_LED0H + 2 * (idx)) ++#define XWAY_MMD_LEDxL(idx) (XWAY_MMD_LED0L + 2 * (idx)) ++ + #define PHY_ID_PHY11G_1_3 0x030260D1 + #define PHY_ID_PHY22F_1_3 0x030260E1 + #define PHY_ID_PHY11G_1_4 0xD565A400 +@@ -229,20 +236,12 @@ static int xway_gphy_rgmii_init(struct p + XWAY_MDIO_MIICTRL_TXSKEW_MASK, val); + } + +-static int xway_gphy_config_init(struct phy_device *phydev) ++static int xway_gphy_init_leds(struct phy_device *phydev) + { + int err; + u32 ledxh; + u32 ledxl; + +- /* Mask all interrupts */ +- err = phy_write(phydev, XWAY_MDIO_IMASK, 0); +- if (err) +- return err; +- +- /* Clear all pending interrupts */ +- phy_read(phydev, XWAY_MDIO_ISTAT); +- + /* Ensure that integrated led function is enabled for all leds */ + err = phy_write(phydev, XWAY_MDIO_LED, + XWAY_MDIO_LED_LED0_EN | +@@ -276,6 +275,26 @@ static int xway_gphy_config_init(struct + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl); + ++ return 0; ++} ++ ++static int xway_gphy_config_init(struct phy_device *phydev) ++{ ++ struct device_node *np = phydev->mdio.dev.of_node; ++ int err; ++ ++ /* Mask all interrupts */ ++ err = phy_write(phydev, XWAY_MDIO_IMASK, 0); ++ if (err) ++ return err; ++ ++ /* Use default LED configuration if 'leds' node isn't defined */ ++ if (!of_get_child_by_name(np, "leds")) ++ xway_gphy_init_leds(phydev); ++ ++ /* Clear all pending interrupts */ ++ phy_read(phydev, XWAY_MDIO_ISTAT); ++ + err = xway_gphy_rgmii_init(phydev); + if (err) + return err; +@@ -347,6 +366,172 @@ static irqreturn_t xway_gphy_handle_inte + return IRQ_HANDLED; + } + ++static int xway_gphy_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ int ret; ++ ++ if (index >= XWAY_GPHY_MAX_LEDS) ++ return -EINVAL; ++ ++ /* clear EN and set manual LED state */ ++ ret = phy_modify(phydev, XWAY_MDIO_LED, ++ ((value == LED_OFF) ? XWAY_GPHY_LED_EN(index) : 0) | ++ XWAY_GPHY_LED_DA(index), ++ (value == LED_OFF) ? 0 : XWAY_GPHY_LED_DA(index)); ++ if (ret) ++ return ret; ++ ++ /* clear HW LED setup */ ++ if (value == LED_OFF) { ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index), 0); ++ if (ret) ++ return ret; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index), 0); ++ } else { ++ return 0; ++ } ++} ++ ++static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX)); ++ ++static int xway_gphy_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ if (index >= XWAY_GPHY_MAX_LEDS) ++ return -EINVAL; ++ ++ /* activity triggers are not possible without combination with a link ++ * trigger. ++ */ ++ if (rules & (BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX)) && ++ !(rules & (BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000)))) ++ return -EOPNOTSUPP; ++ ++ /* All other combinations of the supported triggers are allowed */ ++ if (rules & ~supported_triggers) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++ ++static int xway_gphy_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ int lval, hval; ++ ++ if (index >= XWAY_GPHY_MAX_LEDS) ++ return -EINVAL; ++ ++ hval = phy_read_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index)); ++ if (hval < 0) ++ return hval; ++ ++ lval = phy_read_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index)); ++ if (lval < 0) ++ return lval; ++ ++ if (hval & XWAY_MMD_LEDxH_CON_LINK10) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_10); ++ ++ if (hval & XWAY_MMD_LEDxH_CON_LINK100) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_100); ++ ++ if (hval & XWAY_MMD_LEDxH_CON_LINK1000) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_1000); ++ ++ if ((hval & XWAY_MMD_LEDxH_CON_LINK10) && ++ (hval & XWAY_MMD_LEDxH_CON_LINK100) && ++ (hval & XWAY_MMD_LEDxH_CON_LINK1000)) ++ *rules |= BIT(TRIGGER_NETDEV_LINK); ++ ++ if (lval & XWAY_MMD_LEDxL_PULSE_TXACT) ++ *rules |= BIT(TRIGGER_NETDEV_TX); ++ ++ if (lval & XWAY_MMD_LEDxL_PULSE_RXACT) ++ *rules |= BIT(TRIGGER_NETDEV_RX); ++ ++ return 0; ++} ++ ++static int xway_gphy_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 hval = 0, lval = 0; ++ int ret; ++ ++ if (index >= XWAY_GPHY_MAX_LEDS) ++ return -EINVAL; ++ ++ if (rules & BIT(TRIGGER_NETDEV_LINK) || ++ rules & BIT(TRIGGER_NETDEV_LINK_10)) ++ hval |= XWAY_MMD_LEDxH_CON_LINK10; ++ ++ if (rules & BIT(TRIGGER_NETDEV_LINK) || ++ rules & BIT(TRIGGER_NETDEV_LINK_100)) ++ hval |= XWAY_MMD_LEDxH_CON_LINK100; ++ ++ if (rules & BIT(TRIGGER_NETDEV_LINK) || ++ rules & BIT(TRIGGER_NETDEV_LINK_1000)) ++ hval |= XWAY_MMD_LEDxH_CON_LINK1000; ++ ++ if (rules & BIT(TRIGGER_NETDEV_TX)) ++ lval |= XWAY_MMD_LEDxL_PULSE_TXACT; ++ ++ if (rules & BIT(TRIGGER_NETDEV_RX)) ++ lval |= XWAY_MMD_LEDxL_PULSE_RXACT; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index), hval); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index), lval); ++ if (ret) ++ return ret; ++ ++ return phy_set_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_EN(index)); ++} ++ ++static int xway_gphy_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ bool force_active_low = false, force_active_high = false; ++ u32 mode; ++ ++ if (index >= XWAY_GPHY_MAX_LEDS) ++ return -EINVAL; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ force_active_low = true; ++ break; ++ case PHY_LED_ACTIVE_HIGH: ++ force_active_high = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ if (force_active_low) ++ return phy_set_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index)); ++ ++ if (force_active_high) ++ return phy_clear_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index)); ++ ++ unreachable(); ++} ++ + static struct phy_driver xway_gphy[] = { + { + .phy_id = PHY_ID_PHY11G_1_3, +@@ -359,6 +544,11 @@ static struct phy_driver xway_gphy[] = { + .config_intr = xway_gphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, ++ .led_brightness_set = xway_gphy_led_brightness_set, ++ .led_hw_is_supported = xway_gphy_led_hw_is_supported, ++ .led_hw_control_get = xway_gphy_led_hw_control_get, ++ .led_hw_control_set = xway_gphy_led_hw_control_set, ++ .led_polarity_set = xway_gphy_led_polarity_set, + }, { + .phy_id = PHY_ID_PHY22F_1_3, + .phy_id_mask = 0xffffffff, +@@ -370,6 +560,11 @@ static struct phy_driver xway_gphy[] = { + .config_intr = xway_gphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, ++ .led_brightness_set = xway_gphy_led_brightness_set, ++ .led_hw_is_supported = xway_gphy_led_hw_is_supported, ++ .led_hw_control_get = xway_gphy_led_hw_control_get, ++ .led_hw_control_set = xway_gphy_led_hw_control_set, ++ .led_polarity_set = xway_gphy_led_polarity_set, + }, { + .phy_id = PHY_ID_PHY11G_1_4, + .phy_id_mask = 0xffffffff, +@@ -381,6 +576,11 @@ static struct phy_driver xway_gphy[] = { + .config_intr = xway_gphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, ++ .led_brightness_set = xway_gphy_led_brightness_set, ++ .led_hw_is_supported = xway_gphy_led_hw_is_supported, ++ .led_hw_control_get = xway_gphy_led_hw_control_get, ++ .led_hw_control_set = xway_gphy_led_hw_control_set, ++ .led_polarity_set = xway_gphy_led_polarity_set, + }, { + .phy_id = PHY_ID_PHY22F_1_4, + .phy_id_mask = 0xffffffff, +@@ -392,6 +592,11 @@ static struct phy_driver xway_gphy[] = { + .config_intr = xway_gphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, ++ .led_brightness_set = xway_gphy_led_brightness_set, ++ .led_hw_is_supported = xway_gphy_led_hw_is_supported, ++ .led_hw_control_get = xway_gphy_led_hw_control_get, ++ .led_hw_control_set = xway_gphy_led_hw_control_set, ++ .led_polarity_set = xway_gphy_led_polarity_set, + }, { + .phy_id = PHY_ID_PHY11G_1_5, + .phy_id_mask = 0xffffffff, +@@ -402,6 +607,11 @@ static struct phy_driver xway_gphy[] = { + .config_intr = xway_gphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, ++ .led_brightness_set = xway_gphy_led_brightness_set, ++ .led_hw_is_supported = xway_gphy_led_hw_is_supported, ++ .led_hw_control_get = xway_gphy_led_hw_control_get, ++ .led_hw_control_set = xway_gphy_led_hw_control_set, ++ .led_polarity_set = xway_gphy_led_polarity_set, + }, { + .phy_id = PHY_ID_PHY22F_1_5, + .phy_id_mask = 0xffffffff, +@@ -412,6 +622,11 @@ static struct phy_driver xway_gphy[] = { + .config_intr = xway_gphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, ++ .led_brightness_set = xway_gphy_led_brightness_set, ++ .led_hw_is_supported = xway_gphy_led_hw_is_supported, ++ .led_hw_control_get = xway_gphy_led_hw_control_get, ++ .led_hw_control_set = xway_gphy_led_hw_control_set, ++ .led_polarity_set = xway_gphy_led_polarity_set, + }, { + .phy_id = PHY_ID_PHY11G_VR9_1_1, + .phy_id_mask = 0xffffffff, +@@ -422,6 +637,11 @@ static struct phy_driver xway_gphy[] = { + .config_intr = xway_gphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, ++ .led_brightness_set = xway_gphy_led_brightness_set, ++ .led_hw_is_supported = xway_gphy_led_hw_is_supported, ++ .led_hw_control_get = xway_gphy_led_hw_control_get, ++ .led_hw_control_set = xway_gphy_led_hw_control_set, ++ .led_polarity_set = xway_gphy_led_polarity_set, + }, { + .phy_id = PHY_ID_PHY22F_VR9_1_1, + .phy_id_mask = 0xffffffff, +@@ -432,6 +652,11 @@ static struct phy_driver xway_gphy[] = { + .config_intr = xway_gphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, ++ .led_brightness_set = xway_gphy_led_brightness_set, ++ .led_hw_is_supported = xway_gphy_led_hw_is_supported, ++ .led_hw_control_get = xway_gphy_led_hw_control_get, ++ .led_hw_control_set = xway_gphy_led_hw_control_set, ++ .led_polarity_set = xway_gphy_led_polarity_set, + }, { + .phy_id = PHY_ID_PHY11G_VR9_1_2, + .phy_id_mask = 0xffffffff, +@@ -442,6 +667,11 @@ static struct phy_driver xway_gphy[] = { + .config_intr = xway_gphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, ++ .led_brightness_set = xway_gphy_led_brightness_set, ++ .led_hw_is_supported = xway_gphy_led_hw_is_supported, ++ .led_hw_control_get = xway_gphy_led_hw_control_get, ++ .led_hw_control_set = xway_gphy_led_hw_control_set, ++ .led_polarity_set = xway_gphy_led_polarity_set, + }, { + .phy_id = PHY_ID_PHY22F_VR9_1_2, + .phy_id_mask = 0xffffffff, +@@ -452,6 +682,11 @@ static struct phy_driver xway_gphy[] = { + .config_intr = xway_gphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, ++ .led_brightness_set = xway_gphy_led_brightness_set, ++ .led_hw_is_supported = xway_gphy_led_hw_is_supported, ++ .led_hw_control_get = xway_gphy_led_hw_control_get, ++ .led_hw_control_set = xway_gphy_led_hw_control_set, ++ .led_polarity_set = xway_gphy_led_polarity_set, + }, + }; + module_phy_driver(xway_gphy); diff --git a/target/linux/generic/backport-6.12/730-09-v6.18-net-phy-mxl-gpy-fix-link-properties-on-USXGMII-and-i.patch b/target/linux/generic/backport-6.12/730-09-v6.18-net-phy-mxl-gpy-fix-link-properties-on-USXGMII-and-i.patch new file mode 100644 index 00000000000..6f3bbfcc0e4 --- /dev/null +++ b/target/linux/generic/backport-6.12/730-09-v6.18-net-phy-mxl-gpy-fix-link-properties-on-USXGMII-and-i.patch @@ -0,0 +1,56 @@ +From 081156ce13f8fa4e97b5148dc54d8c0ddf02117b Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 20 Nov 2025 15:02:19 +0000 +Subject: [PATCH] net: phy: mxl-gpy: fix link properties on USXGMII and + internal PHYs + +gpy_update_interface() returns early in case the PHY is internal or +connected via USXGMII. In this case the gigabit master/slave property +as well as MDI/MDI-X status also won't be read which seems wrong. +Always read those properties by moving the logic to retrieve them to +gpy_read_status(). + +Fixes: fd8825cd8c6fc ("net: phy: mxl-gpy: Add PHY Auto/MDI/MDI-X set driver for GPY211 chips") +Fixes: 311abcdddc00a ("net: phy: add support to get Master-Slave configuration") +Suggested-by: "Russell King (Oracle)" +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/71fccf3f56742116eb18cc070d2a9810479ea7f9.1763650701.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/mxl-gpy.c | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -584,13 +584,7 @@ static int gpy_update_interface(struct p + break; + } + +- if (phydev->speed == SPEED_2500 || phydev->speed == SPEED_1000) { +- ret = genphy_read_master_slave(phydev); +- if (ret < 0) +- return ret; +- } +- +- return gpy_update_mdix(phydev); ++ return 0; + } + + static int gpy_read_status(struct phy_device *phydev) +@@ -645,6 +639,16 @@ static int gpy_read_status(struct phy_de + ret = gpy_update_interface(phydev); + if (ret < 0) + return ret; ++ ++ if (phydev->speed == SPEED_2500 || phydev->speed == SPEED_1000) { ++ ret = genphy_read_master_slave(phydev); ++ if (ret < 0) ++ return ret; ++ } ++ ++ ret = gpy_update_mdix(phydev); ++ if (ret < 0) ++ return ret; + } + + return 0; diff --git a/target/linux/generic/backport-6.12/730-10-v6.19-net-phy-mxl-gpy-add-support-for-MxL86211C.patch b/target/linux/generic/backport-6.12/730-10-v6.19-net-phy-mxl-gpy-add-support-for-MxL86211C.patch new file mode 100644 index 00000000000..77073377c9f --- /dev/null +++ b/target/linux/generic/backport-6.12/730-10-v6.19-net-phy-mxl-gpy-add-support-for-MxL86211C.patch @@ -0,0 +1,64 @@ +From 9d844da693d6d0813714d9b5b7a58ac05c4cf7f0 Mon Sep 17 00:00:00 2001 +From: Chad Monroe +Date: Sat, 22 Nov 2025 13:32:15 +0000 +Subject: [PATCH] net: phy: mxl-gpy: add support for MxL86211C + +MxL86211C is a smaller and more efficient version of the GPY211C. +Add the PHY ID and phy_driver instance to the mxl-gpy driver. + +Signed-off-by: Chad Monroe +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/cabf3559d6511bed6b8a925f540e3162efc20f6b.1763818120.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mxl-gpy.c | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -30,6 +30,7 @@ + #define PHY_ID_GPY241B 0x67C9DE40 + #define PHY_ID_GPY241BM 0x67C9DE80 + #define PHY_ID_GPY245B 0x67C9DEC0 ++#define PHY_ID_MXL86211C 0xC1335400 + + #define PHY_CTL1 0x13 + #define PHY_CTL1_MDICD BIT(3) +@@ -1275,6 +1276,28 @@ static struct phy_driver gpy_drivers[] = + .get_wol = gpy_get_wol, + .set_loopback = gpy_loopback, + }, ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_MXL86211C), ++ .name = "Maxlinear Ethernet MxL86211C", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, ++ }, + }; + module_phy_driver(gpy_drivers); + +@@ -1291,6 +1314,7 @@ static const struct mdio_device_id __may + {PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)}, + {PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)}, + {PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)}, ++ {PHY_ID_MATCH_MODEL(PHY_ID_MXL86211C)}, + { } + }; + MODULE_DEVICE_TABLE(mdio, gpy_tbl); diff --git a/target/linux/generic/backport-6.12/730-11-v7.0-net-phy-mxl-gpy-implement-SGMII-in-band-configuratio.patch b/target/linux/generic/backport-6.12/730-11-v7.0-net-phy-mxl-gpy-implement-SGMII-in-band-configuratio.patch new file mode 100644 index 00000000000..0e99bfe7c55 --- /dev/null +++ b/target/linux/generic/backport-6.12/730-11-v7.0-net-phy-mxl-gpy-implement-SGMII-in-band-configuratio.patch @@ -0,0 +1,187 @@ +From 9da9633f2f02df7da67ab3b6f84eda4956ae1c5a Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 7 Jan 2026 15:39:16 +0000 +Subject: [PATCH] net: phy: mxl-gpy: implement SGMII in-band configuration + +SGMII in-band autonegotiation was previously kept untouched (and restored +after switching back from 2500Base-X to SGMII). Now that the kernel offers +a way to announce in-band capabilities and nable/disable in-band AN, +implement the .inband_caps and .config_inband driver ops. +This moves the responsibility to configure SGMII in-band AN from the PHY +driver to phylink. + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/70f07e46dd96e239a9711e6073e8c04c1d8672d4.1767800226.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mxl-gpy.c | 61 ++++++++++++++++++++++++++++++--------- + 1 file changed, 47 insertions(+), 14 deletions(-) + +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -568,20 +568,6 @@ static int gpy_update_interface(struct p + case SPEED_100: + case SPEED_10: + phydev->interface = PHY_INTERFACE_MODE_SGMII; +- if (gpy_sgmii_aneg_en(phydev)) +- break; +- /* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed +- * if ANEG is disabled (in 2500-BaseX mode). +- */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, +- VSPEC1_SGMII_ANEN_ANRS, +- VSPEC1_SGMII_ANEN_ANRS); +- if (ret < 0) { +- phydev_err(phydev, +- "Error: Enable of SGMII ANEG failed: %d\n", +- ret); +- return ret; +- } + break; + } + +@@ -1022,6 +1008,27 @@ static int gpy_led_polarity_set(struct p + unreachable(); + } + ++static unsigned int gpy_inband_caps(struct phy_device *phydev, ++ phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return LINK_INBAND_DISABLE; ++ default: ++ return 0; ++ } ++} ++ ++static int gpy_config_inband(struct phy_device *phydev, unsigned int modes) ++{ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, ++ VSPEC1_SGMII_ANEN_ANRS, ++ (modes == LINK_INBAND_DISABLE) ? 0 : ++ VSPEC1_SGMII_ANEN_ANRS); ++} ++ + static struct phy_driver gpy_drivers[] = { + { + PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx), +@@ -1029,6 +1036,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1052,6 +1061,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1074,6 +1085,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1097,6 +1110,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy21x_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1119,6 +1134,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy21x_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1141,6 +1158,8 @@ static struct phy_driver gpy_drivers[] = + .name = "Maxlinear Ethernet GPY212B", + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy21x_config_init, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .probe = gpy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, +@@ -1164,6 +1183,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy21x_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1187,6 +1208,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy21x_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1209,6 +1232,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy21x_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1231,6 +1256,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1248,6 +1275,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1265,6 +1294,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, +@@ -1282,6 +1313,8 @@ static struct phy_driver gpy_drivers[] = + .get_features = genphy_c45_pma_read_abilities, + .config_init = gpy_config_init, + .probe = gpy_probe, ++ .inband_caps = gpy_inband_caps, ++ .config_inband = gpy_config_inband, + .suspend = genphy_suspend, + .resume = genphy_resume, + .config_aneg = gpy_config_aneg, diff --git a/target/linux/generic/backport-6.12/731-v6.18-net-mediatek-wed-Introduce-MT7992-WED-support-to-MT7.patch b/target/linux/generic/backport-6.12/731-v6.18-net-mediatek-wed-Introduce-MT7992-WED-support-to-MT7.patch new file mode 100644 index 00000000000..8b1844df67c --- /dev/null +++ b/target/linux/generic/backport-6.12/731-v6.18-net-mediatek-wed-Introduce-MT7992-WED-support-to-MT7.patch @@ -0,0 +1,116 @@ +From: Lorenzo Bianconi +Date: Tue, 12 Aug 2025 06:57:23 +0200 +Subject: [PATCH] net: mediatek: wed: Introduce MT7992 WED support to MT7988 + SoC + +Introduce the second WDMA RX ring in WED driver for MT7988 SoC since the +Mediatek MT7992 WiFi chipset supports two separated WDMA rings. +Add missing MT7988 configurations to properly support WED for MT7992 in +MT76 driver. + +Co-developed-by: Rex Lu +Signed-off-by: Rex Lu +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250812-mt7992-wed-support-v3-1-9ada78a819a4@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -59,7 +59,9 @@ struct mtk_wed_flow_block_priv { + static const struct mtk_wed_soc_data mt7622_data = { + .regmap = { + .tx_bm_tkid = 0x088, +- .wpdma_rx_ring0 = 0x770, ++ .wpdma_rx_ring = { ++ 0x770, ++ }, + .reset_idx_tx_mask = GENMASK(3, 0), + .reset_idx_rx_mask = GENMASK(17, 16), + }, +@@ -70,7 +72,9 @@ static const struct mtk_wed_soc_data mt7 + static const struct mtk_wed_soc_data mt7986_data = { + .regmap = { + .tx_bm_tkid = 0x0c8, +- .wpdma_rx_ring0 = 0x770, ++ .wpdma_rx_ring = { ++ 0x770, ++ }, + .reset_idx_tx_mask = GENMASK(1, 0), + .reset_idx_rx_mask = GENMASK(7, 6), + }, +@@ -81,7 +85,10 @@ static const struct mtk_wed_soc_data mt7 + static const struct mtk_wed_soc_data mt7988_data = { + .regmap = { + .tx_bm_tkid = 0x0c8, +- .wpdma_rx_ring0 = 0x7d0, ++ .wpdma_rx_ring = { ++ 0x7d0, ++ 0x7d8, ++ }, + .reset_idx_tx_mask = GENMASK(1, 0), + .reset_idx_rx_mask = GENMASK(7, 6), + }, +@@ -621,8 +628,8 @@ mtk_wed_amsdu_init(struct mtk_wed_device + return ret; + } + +- /* eagle E1 PCIE1 tx ring 22 flow control issue */ +- if (dev->wlan.id == 0x7991) ++ /* Kite and Eagle E1 PCIE1 tx ring 22 flow control issue */ ++ if (dev->wlan.id == 0x7991 || dev->wlan.id == 0x7992) + wed_clr(dev, MTK_WED_AMSDU_FIFO, MTK_WED_AMSDU_IS_PRIOR0_RING); + + wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN); +@@ -1239,7 +1246,11 @@ mtk_wed_set_wpdma(struct mtk_wed_device + return; + + wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo); +- wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx); ++ wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring[0], ++ dev->wlan.wpdma_rx[0]); ++ if (mtk_wed_is_v3_or_greater(dev->hw)) ++ wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring[1], ++ dev->wlan.wpdma_rx[1]); + + if (!dev->wlan.hw_rro) + return; +@@ -2335,6 +2346,16 @@ mtk_wed_start(struct mtk_wed_device *dev + if (!dev->rx_wdma[i].desc) + mtk_wed_wdma_rx_ring_setup(dev, i, 16, false); + ++ if (dev->wlan.hw_rro) { ++ for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) { ++ u32 addr = MTK_WED_RRO_MSDU_PG_CTRL0(i) + ++ MTK_WED_RING_OFS_COUNT; ++ ++ if (!wed_r32(dev, addr)) ++ wed_w32(dev, addr, 1); ++ } ++ } ++ + mtk_wed_hw_init(dev); + mtk_wed_configure_irq(dev, irq_mask); + +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -17,7 +17,7 @@ struct mtk_wed_wo; + struct mtk_wed_soc_data { + struct { + u32 tx_bm_tkid; +- u32 wpdma_rx_ring0; ++ u32 wpdma_rx_ring[MTK_WED_RX_QUEUES]; + u32 reset_idx_tx_mask; + u32 reset_idx_rx_mask; + } regmap; +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -147,7 +147,7 @@ struct mtk_wed_device { + u32 wpdma_tx; + u32 wpdma_txfree; + u32 wpdma_rx_glo; +- u32 wpdma_rx; ++ u32 wpdma_rx[MTK_WED_RX_QUEUES]; + u32 wpdma_rx_rro[MTK_WED_RX_QUEUES]; + u32 wpdma_rx_pg; + diff --git a/target/linux/generic/backport-6.12/732-v6.18-wifi-mt76-wed-use-proper-wed-reference-in-mt76-wed-d.patch b/target/linux/generic/backport-6.12/732-v6.18-wifi-mt76-wed-use-proper-wed-reference-in-mt76-wed-d.patch new file mode 100644 index 00000000000..883de549cd6 --- /dev/null +++ b/target/linux/generic/backport-6.12/732-v6.18-wifi-mt76-wed-use-proper-wed-reference-in-mt76-wed-d.patch @@ -0,0 +1,79 @@ +From: Lorenzo Bianconi +Date: Wed, 8 Oct 2025 12:41:48 +0200 +Subject: [PATCH] wifi: mt76: wed: use proper wed reference in mt76 wed driver + callabacks + +MT7996 driver can use both wed and wed_hif2 devices to offload traffic +from/to the wireless NIC. In the current codebase we assume to always +use the primary wed device in wed callbacks resulting in the following +crash if the hw runs wed_hif2 (e.g. 6GHz link). + +[ 297.455876] Unable to handle kernel read from unreadable memory at virtual address 000000000000080a +[ 297.464928] Mem abort info: +[ 297.467722] ESR = 0x0000000096000005 +[ 297.471461] EC = 0x25: DABT (current EL), IL = 32 bits +[ 297.476766] SET = 0, FnV = 0 +[ 297.479809] EA = 0, S1PTW = 0 +[ 297.482940] FSC = 0x05: level 1 translation fault +[ 297.487809] Data abort info: +[ 297.490679] ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000 +[ 297.496156] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 +[ 297.501196] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 +[ 297.506500] user pgtable: 4k pages, 39-bit VAs, pgdp=0000000107480000 +[ 297.512927] [000000000000080a] pgd=08000001097fb003, p4d=08000001097fb003, pud=08000001097fb003, pmd=0000000000000000 +[ 297.523532] Internal error: Oops: 0000000096000005 [#1] SMP +[ 297.715393] CPU: 2 UID: 0 PID: 45 Comm: kworker/u16:2 Tainted: G O 6.12.50 #0 +[ 297.723908] Tainted: [O]=OOT_MODULE +[ 297.727384] Hardware name: Banana Pi BPI-R4 (2x SFP+) (DT) +[ 297.732857] Workqueue: nf_ft_offload_del nf_flow_rule_route_ipv6 [nf_flow_table] +[ 297.740254] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) +[ 297.747205] pc : mt76_wed_offload_disable+0x64/0xa0 [mt76] +[ 297.752688] lr : mtk_wed_flow_remove+0x58/0x80 +[ 297.757126] sp : ffffffc080fe3ae0 +[ 297.760430] x29: ffffffc080fe3ae0 x28: ffffffc080fe3be0 x27: 00000000deadbef7 +[ 297.767557] x26: ffffff80c5ebca00 x25: 0000000000000001 x24: ffffff80c85f4c00 +[ 297.774683] x23: ffffff80c1875b78 x22: ffffffc080d42cd0 x21: ffffffc080660018 +[ 297.781809] x20: ffffff80c6a076d0 x19: ffffff80c6a043c8 x18: 0000000000000000 +[ 297.788935] x17: 0000000000000000 x16: 0000000000000001 x15: 0000000000000000 +[ 297.796060] x14: 0000000000000019 x13: ffffff80c0ad8ec0 x12: 00000000fa83b2da +[ 297.803185] x11: ffffff80c02700c0 x10: ffffff80c0ad8ec0 x9 : ffffff81fef96200 +[ 297.810311] x8 : ffffff80c02700c0 x7 : ffffff80c02700d0 x6 : 0000000000000002 +[ 297.817435] x5 : 0000000000000400 x4 : 0000000000000000 x3 : 0000000000000000 +[ 297.824561] x2 : 0000000000000001 x1 : 0000000000000800 x0 : ffffff80c6a063c8 +[ 297.831686] Call trace: +[ 297.834123] mt76_wed_offload_disable+0x64/0xa0 [mt76] +[ 297.839254] mtk_wed_flow_remove+0x58/0x80 +[ 297.843342] mtk_flow_offload_cmd+0x434/0x574 +[ 297.847689] mtk_wed_setup_tc_block_cb+0x30/0x40 +[ 297.852295] nf_flow_offload_ipv6_hook+0x7f4/0x964 [nf_flow_table] +[ 297.858466] nf_flow_rule_route_ipv6+0x438/0x4a4 [nf_flow_table] +[ 297.864463] process_one_work+0x174/0x300 +[ 297.868465] worker_thread+0x278/0x430 +[ 297.872204] kthread+0xd8/0xdc +[ 297.875251] ret_from_fork+0x10/0x20 +[ 297.878820] Code: 928b5ae0 8b000273 91400a60 f943fa61 (79401421) +[ 297.884901] ---[ end trace 0000000000000000 ]--- + +Fix the issue detecting the proper wed reference to use running wed +callabacks. + +-- Partial backport for data structure change only (rest is in the mt76 package) + +Fixes: 83eafc9251d6 ("wifi: mt76: mt7996: add wed tx support") +Tested-by: Daniel Pawlik +Tested-by: Matteo Croce +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251008-wed-fixes-v1-1-8f7678583385@kernel.org +Signed-off-by: Felix Fietkau +--- + +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -154,6 +154,7 @@ struct mtk_wed_device { + bool wcid_512; + bool hw_rro; + bool msi; ++ bool hif2; + + u16 token_start; + unsigned int nbuf; diff --git a/target/linux/generic/backport-6.12/733-v6.18-net-mtk-wed-add-dma-mask-limitation-and-GFP_DMA32-fo.patch b/target/linux/generic/backport-6.12/733-v6.18-net-mtk-wed-add-dma-mask-limitation-and-GFP_DMA32-fo.patch new file mode 100644 index 00000000000..c1bb1b972b9 --- /dev/null +++ b/target/linux/generic/backport-6.12/733-v6.18-net-mtk-wed-add-dma-mask-limitation-and-GFP_DMA32-fo.patch @@ -0,0 +1,50 @@ +From: Rex Lu +Date: Thu, 9 Oct 2025 08:29:34 +0200 +Subject: [PATCH] net: mtk: wed: add dma mask limitation and GFP_DMA32 for + device with more than 4GB DRAM + +Limit tx/rx buffer address to 32-bit address space for board with more +than 4GB DRAM. + +Fixes: 804775dfc2885 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)") +Fixes: 6757d345dd7db ("net: ethernet: mtk_wed: introduce hw_rro support for MT7988") +Tested-by: Daniel Pawlik +Tested-by: Matteo Croce +Signed-off-by: Rex Lu +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -677,7 +677,7 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d + void *buf; + int s; + +- page = __dev_alloc_page(GFP_KERNEL); ++ page = __dev_alloc_page(GFP_KERNEL | GFP_DMA32); + if (!page) + return -ENOMEM; + +@@ -800,7 +800,7 @@ mtk_wed_hwrro_buffer_alloc(struct mtk_we + struct page *page; + int s; + +- page = __dev_alloc_page(GFP_KERNEL); ++ page = __dev_alloc_page(GFP_KERNEL | GFP_DMA32); + if (!page) + return -ENOMEM; + +@@ -2438,6 +2438,10 @@ mtk_wed_attach(struct mtk_wed_device *de + dev->version = hw->version; + dev->hw->pcie_base = mtk_wed_get_pcie_base(dev); + ++ ret = dma_set_mask_and_coherent(hw->dev, DMA_BIT_MASK(32)); ++ if (ret) ++ goto out; ++ + if (hw->eth->dma_dev == hw->eth->dev && + of_dma_is_coherent(hw->eth->dev->of_node)) + mtk_eth_set_dma_device(hw->eth, hw->dev); diff --git a/target/linux/generic/backport-6.12/735-v6.13-net-phy-avoid-undefined-behavior-in-_led_polarity_se.patch b/target/linux/generic/backport-6.12/735-v6.13-net-phy-avoid-undefined-behavior-in-_led_polarity_se.patch new file mode 100644 index 00000000000..622d2c3b540 --- /dev/null +++ b/target/linux/generic/backport-6.12/735-v6.13-net-phy-avoid-undefined-behavior-in-_led_polarity_se.patch @@ -0,0 +1,64 @@ +From cff865c700711ecc3824b2dfe181637f3ed23c80 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Tue, 17 Dec 2024 09:10:34 +0100 +Subject: net: phy: avoid undefined behavior in *_led_polarity_set() + +gcc runs into undefined behavior at the end of the three led_polarity_set() +callback functions if it were called with a zero 'modes' argument and it +just ends the function there without returning from it. + +This gets flagged by 'objtool' as a function that continues on +to the next one: + +drivers/net/phy/aquantia/aquantia_leds.o: warning: objtool: aqr_phy_led_polarity_set+0xf: can't find jump dest instruction at .text+0x5d9 +drivers/net/phy/intel-xway.o: warning: objtool: xway_gphy_led_polarity_set() falls through to next function xway_gphy_config_init() +drivers/net/phy/mxl-gpy.o: warning: objtool: gpy_led_polarity_set() falls through to next function gpy_led_hw_control_get() + +There is no point to micro-optimize the behavior here to save a single-digit +number of bytes in the kernel, so just change this to a "return -EINVAL" +as we do when any unexpected bits are set. + +Fixes: 1758af47b98c ("net: phy: intel-xway: add support for PHY LEDs") +Fixes: 9d55e68b19f2 ("net: phy: aquantia: correctly describe LED polarity override") +Fixes: eb89c79c1b8f ("net: phy: mxl-gpy: correctly describe LED polarity") +Signed-off-by: Arnd Bergmann +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20241217081056.238792-1-arnd@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/aquantia/aquantia_leds.c | 2 +- + drivers/net/phy/intel-xway.c | 2 +- + drivers/net/phy/mxl-gpy.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/aquantia/aquantia_leds.c ++++ b/drivers/net/phy/aquantia/aquantia_leds.c +@@ -156,5 +156,5 @@ int aqr_phy_led_polarity_set(struct phy_ + if (force_active_high || force_active_low) + return aqr_phy_led_active_low_set(phydev, index, force_active_low); + +- unreachable(); ++ return -EINVAL; + } +--- a/drivers/net/phy/intel-xway.c ++++ b/drivers/net/phy/intel-xway.c +@@ -529,7 +529,7 @@ static int xway_gphy_led_polarity_set(st + if (force_active_high) + return phy_clear_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index)); + +- unreachable(); ++ return -EINVAL; + } + + static struct phy_driver xway_gphy[] = { +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -1005,7 +1005,7 @@ static int gpy_led_polarity_set(struct p + if (force_active_high) + return phy_clear_bits(phydev, PHY_LED, PHY_LED_POLARITY(index)); + +- unreachable(); ++ return -EINVAL; + } + + static unsigned int gpy_inband_caps(struct phy_device *phydev, diff --git a/target/linux/generic/backport-6.12/740-v6.13-net-dsa-mv88e6xxx-Support-LED-control.patch b/target/linux/generic/backport-6.12/740-v6.13-net-dsa-mv88e6xxx-Support-LED-control.patch new file mode 100644 index 00000000000..19268cf5788 --- /dev/null +++ b/target/linux/generic/backport-6.12/740-v6.13-net-dsa-mv88e6xxx-Support-LED-control.patch @@ -0,0 +1,1250 @@ +From 7b590490e3aa6bfa38bf6e2069a529017fd3c1d2 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Fri, 13 Oct 2023 00:08:35 +0200 +Subject: [PATCH] net: dsa: mv88e6xxx: Support LED control + +This adds control over the hardware LEDs in the Marvell +MV88E6xxx DSA switch and enables it for MV88E6352. + +This fixes an imminent problem on the Inteno XG6846 which +has a WAN LED that simply do not work with hardware +defaults: driver amendment is necessary. + +The patch is modeled after Christian Marangis LED support +code for the QCA8k DSA switch, I got help with the register +definitions from Tim Harvey. + +After this patch it is possible to activate hardware link +indication like this (or with a similar script): + + cd /sys/class/leds/Marvell\ 88E6352:05:00:green:wan/ + echo netdev > trigger + echo 1 > link + +This makes the green link indicator come up on any link +speed. It is also possible to be more elaborate, like this: + + cd /sys/class/leds/Marvell\ 88E6352:05:00:green:wan/ + echo netdev > trigger + echo 1 > link_1000 + cd /sys/class/leds/Marvell\ 88E6352:05:01:amber:wan/ + echo netdev > trigger + echo 1 > link_100 + +Making the green LED come on for a gigabit link and the +amber LED come on for a 100 mbit link. + +Each port has 2 LED slots (the hardware may use just one or +none) and the hardware triggers are specified in four bits per +LED, and some of the hardware triggers are only available on the +SFP (fiber) uplink. The restrictions are described in the +port.h header file where the registers are described. For +example, selector 1 set for LED 1 on port 5 or 6 will indicate +Fiber 1000 (gigabit) and activity with a blinking LED, but +ONLY for an SFP connection. If port 5/6 is used with something +not SFP, this selector is a noop: something else need to be +selected. + +After the previous series rewriting the MV88E6xxx DT +bindings to use YAML a "leds" subnode is already valid +for each port, in my scratch device tree it looks like +this: + + leds { + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + function = LED_FUNCTION_LAN; + default-state = "off"; + linux,default-trigger = "netdev"; + }; + led@1 { + reg = <1>; + color = ; + function = LED_FUNCTION_LAN; + default-state = "off"; + }; + }; + +This DT config is not yet configuring everything: when the netdev +default trigger is assigned the hw acceleration callbacks are +not called, and there is no way to set the netdev sub-trigger +type (such as link_1000) from the device tree, such as if you want +a gigabit link indicator. This has to be done from userspace at +this point. + +We add LED operations to all switches in the 6352 family: +6172, 6176, 6240 and 6352. + +Signed-off-by: Linus Walleij +--- + drivers/net/dsa/mv88e6xxx/Kconfig | 10 + + drivers/net/dsa/mv88e6xxx/Makefile | 1 + + drivers/net/dsa/mv88e6xxx/chip.c | 38 +- + drivers/net/dsa/mv88e6xxx/chip.h | 11 + + drivers/net/dsa/mv88e6xxx/leds.c | 839 +++++++++++++++++++++++++++++ + drivers/net/dsa/mv88e6xxx/port.c | 1 + + drivers/net/dsa/mv88e6xxx/port.h | 133 +++++ + 7 files changed, 1031 insertions(+), 2 deletions(-) + create mode 100644 drivers/net/dsa/mv88e6xxx/leds.c + +--- a/drivers/net/dsa/mv88e6xxx/Kconfig ++++ b/drivers/net/dsa/mv88e6xxx/Kconfig +@@ -17,3 +17,13 @@ config NET_DSA_MV88E6XXX_PTP + help + Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch + chips that support it. ++ ++config NET_DSA_MV88E6XXX_LEDS ++ bool "LED support for Marvell 88E6xxx" ++ default y ++ depends on NET_DSA_MV88E6XXX ++ depends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_MV88E6XXX ++ depends on LEDS_TRIGGERS ++ help ++ This enabled support for controlling the LEDs attached to the ++ Marvell 88E6xxx switch chips. +--- a/drivers/net/dsa/mv88e6xxx/Makefile ++++ b/drivers/net/dsa/mv88e6xxx/Makefile +@@ -9,6 +9,7 @@ mv88e6xxx-objs += global2.o + mv88e6xxx-objs += global2_avb.o + mv88e6xxx-objs += global2_scratch.o + mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o ++mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_LEDS) += leds.o + mv88e6xxx-objs += pcs-6185.o + mv88e6xxx-objs += pcs-6352.o + mv88e6xxx-objs += pcs-639x.o +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -3412,14 +3413,43 @@ static int mv88e6xxx_setup_upstream_port + static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) + { + struct device_node *phy_handle = NULL; ++ struct fwnode_handle *ports_fwnode; ++ struct fwnode_handle *port_fwnode; + struct dsa_switch *ds = chip->ds; ++ struct mv88e6xxx_port *p; + struct dsa_port *dp; + int tx_amp; + int err; + u16 reg; ++ u32 val; + +- chip->ports[port].chip = chip; +- chip->ports[port].port = port; ++ p = &chip->ports[port]; ++ p->chip = chip; ++ p->port = port; ++ ++ /* Look up corresponding fwnode if any */ ++ ports_fwnode = device_get_named_child_node(chip->dev, "ethernet-ports"); ++ if (!ports_fwnode) ++ ports_fwnode = device_get_named_child_node(chip->dev, "ports"); ++ if (ports_fwnode) { ++ fwnode_for_each_child_node(ports_fwnode, port_fwnode) { ++ if (fwnode_property_read_u32(port_fwnode, "reg", &val)) ++ continue; ++ if (val == port) { ++ p->fwnode = port_fwnode; ++ p->fiber = fwnode_property_present(port_fwnode, "sfp"); ++ break; ++ } ++ } ++ } else { ++ dev_dbg(chip->dev, "no ethernet ports node defined for the device\n"); ++ } ++ ++ if (chip->info->ops->port_setup_leds) { ++ err = chip->info->ops->port_setup_leds(chip, port); ++ if (err && err != -EOPNOTSUPP) ++ return err; ++ } + + err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, + SPEED_UNFORCED, DUPLEX_UNFORCED, +@@ -4653,6 +4683,7 @@ static const struct mv88e6xxx_ops mv88e6 + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_get_cmode = mv88e6352_port_get_cmode, ++ .port_setup_leds = mv88e6xxx_port_setup_leds, + .port_setup_message_port = mv88e6xxx_setup_message_port, + .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, +@@ -4755,6 +4786,7 @@ static const struct mv88e6xxx_ops mv88e6 + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_get_cmode = mv88e6352_port_get_cmode, ++ .port_setup_leds = mv88e6xxx_port_setup_leds, + .port_setup_message_port = mv88e6xxx_setup_message_port, + .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, +@@ -5030,6 +5062,7 @@ static const struct mv88e6xxx_ops mv88e6 + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_get_cmode = mv88e6352_port_get_cmode, ++ .port_setup_leds = mv88e6xxx_port_setup_leds, + .port_setup_message_port = mv88e6xxx_setup_message_port, + .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, +@@ -5460,6 +5493,7 @@ static const struct mv88e6xxx_ops mv88e6 + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_get_cmode = mv88e6352_port_get_cmode, ++ .port_setup_leds = mv88e6xxx_port_setup_leds, + .port_setup_message_port = mv88e6xxx_setup_message_port, + .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, +--- a/drivers/net/dsa/mv88e6xxx/chip.h ++++ b/drivers/net/dsa/mv88e6xxx/chip.h +@@ -13,7 +13,9 @@ + #include + #include + #include ++#include + #include ++#include + #include + #include + #include +@@ -276,6 +278,7 @@ struct mv88e6xxx_vlan { + struct mv88e6xxx_port { + struct mv88e6xxx_chip *chip; + int port; ++ struct fwnode_handle *fwnode; + struct mv88e6xxx_vlan bridge_pvid; + u64 serdes_stats[2]; + u64 atu_member_violation; +@@ -290,6 +293,11 @@ struct mv88e6xxx_port { + struct devlink_region *region; + void *pcs_private; + ++ /* LED related information */ ++ bool fiber; ++ struct led_classdev led0; ++ struct led_classdev led1; ++ + /* MacAuth Bypass control flag */ + bool mab; + }; +@@ -574,6 +582,9 @@ struct mv88e6xxx_ops { + phy_interface_t mode); + int (*port_get_cmode)(struct mv88e6xxx_chip *chip, int port, u8 *cmode); + ++ /* LED control */ ++ int (*port_setup_leds)(struct mv88e6xxx_chip *chip, int port); ++ + /* Some devices have a per port register indicating what is + * the upstream port this port should forward to. + */ +--- /dev/null ++++ b/drivers/net/dsa/mv88e6xxx/leds.c +@@ -0,0 +1,839 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++#include ++#include ++#include ++ ++#include "chip.h" ++#include "global2.h" ++#include "port.h" ++ ++/* Offset 0x16: LED control */ ++ ++static int mv88e6xxx_port_led_write(struct mv88e6xxx_chip *chip, int port, u16 reg) ++{ ++ reg |= MV88E6XXX_PORT_LED_CONTROL_UPDATE; ++ ++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_LED_CONTROL, reg); ++} ++ ++static int mv88e6xxx_port_led_read(struct mv88e6xxx_chip *chip, int port, ++ u16 ptr, u16 *val) ++{ ++ int err; ++ ++ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_LED_CONTROL, ptr); ++ if (err) ++ return err; ++ ++ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_LED_CONTROL, val); ++ *val &= 0x3ff; ++ ++ return err; ++} ++ ++static int mv88e6xxx_led_brightness_set(struct mv88e6xxx_port *p, int led, ++ int brightness) ++{ ++ u16 reg; ++ int err; ++ ++ err = mv88e6xxx_port_led_read(p->chip, p->port, ++ MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL, ++ ®); ++ if (err) ++ return err; ++ ++ if (led == 1) ++ reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK; ++ else ++ reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK; ++ ++ if (brightness) { ++ /* Selector 0x0f == Force LED ON */ ++ if (led == 1) ++ reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELF; ++ else ++ reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELF; ++ } else { ++ /* Selector 0x0e == Force LED OFF */ ++ if (led == 1) ++ reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELE; ++ else ++ reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELE; ++ } ++ ++ reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL; ++ ++ return mv88e6xxx_port_led_write(p->chip, p->port, reg); ++} ++ ++static int mv88e6xxx_led0_brightness_set_blocking(struct led_classdev *ldev, ++ enum led_brightness brightness) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0); ++ int err; ++ ++ mv88e6xxx_reg_lock(p->chip); ++ err = mv88e6xxx_led_brightness_set(p, 0, brightness); ++ mv88e6xxx_reg_unlock(p->chip); ++ ++ return err; ++} ++ ++static int mv88e6xxx_led1_brightness_set_blocking(struct led_classdev *ldev, ++ enum led_brightness brightness) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1); ++ int err; ++ ++ mv88e6xxx_reg_lock(p->chip); ++ err = mv88e6xxx_led_brightness_set(p, 1, brightness); ++ mv88e6xxx_reg_unlock(p->chip); ++ ++ return err; ++} ++ ++struct mv88e6xxx_led_hwconfig { ++ int led; ++ u8 portmask; ++ unsigned long rules; ++ bool fiber; ++ bool blink_activity; ++ u16 selector; ++}; ++ ++/* The following is a lookup table to check what rules we can support on a ++ * certain LED given restrictions such as that some rules only work with fiber ++ * (SFP) connections and some blink on activity by default. ++ */ ++#define MV88E6XXX_PORTS_0_3 (BIT(0) | BIT(1) | BIT(2) | BIT(3)) ++#define MV88E6XXX_PORTS_4_5 (BIT(4) | BIT(5)) ++#define MV88E6XXX_PORT_4 BIT(4) ++#define MV88E6XXX_PORT_5 BIT(5) ++ ++/* Entries are listed in selector order. ++ * ++ * These configurations vary across different switch families, list ++ * different tables per-family here. ++ */ ++static const struct mv88e6xxx_led_hwconfig mv88e6352_led_hwconfigs[] = { ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORT_4, ++ .rules = BIT(TRIGGER_NETDEV_LINK), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL0, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORT_5, ++ .rules = BIT(TRIGGER_NETDEV_LINK_1000), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL0, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_4_5, ++ .rules = BIT(TRIGGER_NETDEV_LINK_100), ++ .blink_activity = true, ++ .fiber = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_4_5, ++ .rules = BIT(TRIGGER_NETDEV_LINK_1000), ++ .blink_activity = true, ++ .fiber = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_1000), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_4_5, ++ .rules = BIT(TRIGGER_NETDEV_LINK_1000), ++ .blink_activity = true, ++ .fiber = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_4_5, ++ .rules = BIT(TRIGGER_NETDEV_LINK_100), ++ .blink_activity = true, ++ .fiber = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL3, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_1000), ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_4_5, ++ .rules = BIT(TRIGGER_NETDEV_LINK), ++ .fiber = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORT_4, ++ .rules = BIT(TRIGGER_NETDEV_LINK), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL4, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORT_5, ++ .rules = BIT(TRIGGER_NETDEV_LINK), ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL5, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORT_4, ++ .rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORT_5, ++ .rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL7, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000), ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL7, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK), ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL8, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORT_5, ++ .rules = BIT(TRIGGER_NETDEV_LINK), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_10), ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL9, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_100), ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL9, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_10), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SELA, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_100), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SELA, ++ }, ++ { ++ .led = 0, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000), ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SELB, ++ }, ++ { ++ .led = 1, ++ .portmask = MV88E6XXX_PORTS_0_3, ++ .rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000), ++ .blink_activity = true, ++ .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SELB, ++ }, ++}; ++ ++/* mv88e6xxx_led_match_selector() - look up the appropriate LED mode selector ++ * @p: port state container ++ * @led: LED number, 0 or 1 ++ * @blink_activity: blink the LED (usually blink on indicated activity) ++ * @fiber: the link is connected to fiber such as SFP ++ * @rules: LED status flags from the LED classdev core ++ * @selector: fill in the selector in this parameter with an OR operation ++ */ ++static int mv88e6xxx_led_match_selector(struct mv88e6xxx_port *p, int led, bool blink_activity, ++ bool fiber, unsigned long rules, u16 *selector) ++{ ++ const struct mv88e6xxx_led_hwconfig *conf; ++ int i; ++ ++ /* No rules means we turn the LED off */ ++ if (!rules) { ++ if (led == 1) ++ *selector |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELE; ++ else ++ *selector |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELE; ++ return 0; ++ } ++ ++ /* TODO: these rules are for MV88E6352, when adding other families, ++ * think about making sure you select the table that match the ++ * specific switch family. ++ */ ++ for (i = 0; i < ARRAY_SIZE(mv88e6352_led_hwconfigs); i++) { ++ conf = &mv88e6352_led_hwconfigs[i]; ++ ++ if (conf->led != led) ++ continue; ++ ++ if (!(conf->portmask & BIT(p->port))) ++ continue; ++ ++ if (conf->blink_activity != blink_activity) ++ continue; ++ ++ if (conf->fiber != fiber) ++ continue; ++ ++ if (conf->rules == rules) { ++ dev_dbg(p->chip->dev, "port%d LED %d set selector %04x for rules %08lx\n", ++ p->port, led, conf->selector, rules); ++ *selector |= conf->selector; ++ return 0; ++ } ++ } ++ ++ return -EOPNOTSUPP; ++} ++ ++/* mv88e6xxx_led_match_selector() - find Linux netdev rules from a selector value ++ * @p: port state container ++ * @selector: the selector value from the LED actity register ++ * @led: LED number, 0 or 1 ++ * @rules: Linux netdev activity rules found from selector ++ */ ++static int ++mv88e6xxx_led_match_rule(struct mv88e6xxx_port *p, u16 selector, int led, unsigned long *rules) ++{ ++ const struct mv88e6xxx_led_hwconfig *conf; ++ int i; ++ ++ /* Find the selector in the table, we just look for the right selector ++ * and ignore if the activity has special properties such as blinking ++ * or is fiber-only. ++ */ ++ for (i = 0; i < ARRAY_SIZE(mv88e6352_led_hwconfigs); i++) { ++ conf = &mv88e6352_led_hwconfigs[i]; ++ ++ if (conf->led != led) ++ continue; ++ ++ if (!(conf->portmask & BIT(p->port))) ++ continue; ++ ++ if (conf->selector == selector) { ++ dev_dbg(p->chip->dev, "port%d LED %d has selector %04x, rules %08lx\n", ++ p->port, led, selector, conf->rules); ++ *rules = conf->rules; ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++/* mv88e6xxx_led_get_selector() - get the appropriate LED mode selector ++ * @p: port state container ++ * @led: LED number, 0 or 1 ++ * @fiber: the link is connected to fiber such as SFP ++ * @rules: LED status flags from the LED classdev core ++ * @selector: fill in the selector in this parameter with an OR operation ++ */ ++static int mv88e6xxx_led_get_selector(struct mv88e6xxx_port *p, int led, ++ bool fiber, unsigned long rules, u16 *selector) ++{ ++ int err; ++ ++ /* What happens here is that we first try to locate a trigger with solid ++ * indicator (such as LED is on for a 1000 link) else we try a second ++ * sweep to find something suitable with a trigger that will blink on ++ * activity. ++ */ ++ err = mv88e6xxx_led_match_selector(p, led, false, fiber, rules, selector); ++ if (err) ++ return mv88e6xxx_led_match_selector(p, led, true, fiber, rules, selector); ++ ++ return 0; ++} ++ ++/* Sets up the hardware blinking period */ ++static int mv88e6xxx_led_set_blinking_period(struct mv88e6xxx_port *p, int led, ++ unsigned long delay_on, unsigned long delay_off) ++{ ++ unsigned long period; ++ u16 reg; ++ ++ period = delay_on + delay_off; ++ ++ reg = 0; ++ ++ switch (period) { ++ case 21: ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_21MS; ++ break; ++ case 42: ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_42MS; ++ break; ++ case 84: ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_84MS; ++ break; ++ case 168: ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_168MS; ++ break; ++ case 336: ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_336MS; ++ break; ++ case 672: ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_672MS; ++ break; ++ default: ++ /* Fall back to software blinking */ ++ return -EINVAL; ++ } ++ ++ /* This is essentially PWM duty cycle: how long time of the period ++ * will the LED be on. Zero isn't great in most cases. ++ */ ++ switch (delay_on) { ++ case 0: ++ /* This is usually pretty useless and will make the LED look OFF */ ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_NONE; ++ break; ++ case 21: ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS; ++ break; ++ case 42: ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_42MS; ++ break; ++ case 84: ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_84MS; ++ break; ++ case 168: ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_168MS; ++ break; ++ default: ++ /* Just use something non-zero */ ++ reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS; ++ break; ++ } ++ ++ /* Set up blink rate */ ++ reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_STRETCH_BLINK; ++ ++ return mv88e6xxx_port_led_write(p->chip, p->port, reg); ++} ++ ++static int mv88e6xxx_led_blink_set(struct mv88e6xxx_port *p, int led, ++ unsigned long *delay_on, unsigned long *delay_off) ++{ ++ u16 reg; ++ int err; ++ ++ /* Choose a sensible default 336 ms (~3 Hz) */ ++ if ((*delay_on == 0) && (*delay_off == 0)) { ++ *delay_on = 168; ++ *delay_off = 168; ++ } ++ ++ /* No off delay is just on */ ++ if (*delay_off == 0) ++ return mv88e6xxx_led_brightness_set(p, led, 1); ++ ++ err = mv88e6xxx_led_set_blinking_period(p, led, *delay_on, *delay_off); ++ if (err) ++ return err; ++ ++ err = mv88e6xxx_port_led_read(p->chip, p->port, ++ MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL, ++ ®); ++ if (err) ++ return err; ++ ++ if (led == 1) ++ reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK; ++ else ++ reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK; ++ ++ /* This will select the forced blinking status */ ++ if (led == 1) ++ reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELD; ++ else ++ reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELD; ++ ++ reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL; ++ ++ return mv88e6xxx_port_led_write(p->chip, p->port, reg); ++} ++ ++static int mv88e6xxx_led0_blink_set(struct led_classdev *ldev, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0); ++ int err; ++ ++ mv88e6xxx_reg_lock(p->chip); ++ err = mv88e6xxx_led_blink_set(p, 0, delay_on, delay_off); ++ mv88e6xxx_reg_unlock(p->chip); ++ ++ return err; ++} ++ ++static int mv88e6xxx_led1_blink_set(struct led_classdev *ldev, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1); ++ int err; ++ ++ mv88e6xxx_reg_lock(p->chip); ++ err = mv88e6xxx_led_blink_set(p, 1, delay_on, delay_off); ++ mv88e6xxx_reg_unlock(p->chip); ++ ++ return err; ++} ++ ++static int ++mv88e6xxx_led0_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0); ++ u16 selector = 0; ++ ++ return mv88e6xxx_led_get_selector(p, 0, p->fiber, rules, &selector); ++} ++ ++static int ++mv88e6xxx_led1_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1); ++ u16 selector = 0; ++ ++ return mv88e6xxx_led_get_selector(p, 1, p->fiber, rules, &selector); ++} ++ ++static int mv88e6xxx_led_hw_control_set(struct mv88e6xxx_port *p, ++ int led, unsigned long rules) ++{ ++ u16 reg; ++ int err; ++ ++ err = mv88e6xxx_port_led_read(p->chip, p->port, ++ MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL, ++ ®); ++ if (err) ++ return err; ++ ++ if (led == 1) ++ reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK; ++ else ++ reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK; ++ ++ err = mv88e6xxx_led_get_selector(p, led, p->fiber, rules, ®); ++ if (err) ++ return err; ++ ++ reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL; ++ ++ if (led == 0) ++ dev_dbg(p->chip->dev, "LED 0 hw control on port %d trigger selector 0x%02x\n", ++ p->port, ++ (unsigned int)(reg & MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK)); ++ else ++ dev_dbg(p->chip->dev, "LED 1 hw control on port %d trigger selector 0x%02x\n", ++ p->port, ++ (unsigned int)(reg & MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK) >> 4); ++ ++ return mv88e6xxx_port_led_write(p->chip, p->port, reg); ++} ++ ++static int ++mv88e6xxx_led_hw_control_get(struct mv88e6xxx_port *p, int led, unsigned long *rules) ++{ ++ u16 val; ++ int err; ++ ++ mv88e6xxx_reg_lock(p->chip); ++ err = mv88e6xxx_port_led_read(p->chip, p->port, ++ MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL, &val); ++ mv88e6xxx_reg_unlock(p->chip); ++ if (err) ++ return err; ++ ++ /* Mask out the selector bits for this port */ ++ if (led == 1) { ++ val &= MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK; ++ /* It's forced blinking/OFF/ON */ ++ if (val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELD || ++ val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELE || ++ val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELF) { ++ *rules = 0; ++ return 0; ++ } ++ } else { ++ val &= MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK; ++ /* It's forced blinking/OFF/ON */ ++ if (val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELD || ++ val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELE || ++ val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELF) { ++ *rules = 0; ++ return 0; ++ } ++ } ++ ++ err = mv88e6xxx_led_match_rule(p, val, led, rules); ++ if (!err) ++ return 0; ++ ++ dev_dbg(p->chip->dev, "couldn't find matching selector for %04x\n", val); ++ *rules = 0; ++ return 0; ++} ++ ++static int ++mv88e6xxx_led0_hw_control_set(struct led_classdev *ldev, unsigned long rules) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0); ++ int err; ++ ++ mv88e6xxx_reg_lock(p->chip); ++ err = mv88e6xxx_led_hw_control_set(p, 0, rules); ++ mv88e6xxx_reg_unlock(p->chip); ++ ++ return err; ++} ++ ++static int ++mv88e6xxx_led1_hw_control_set(struct led_classdev *ldev, unsigned long rules) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1); ++ int err; ++ ++ mv88e6xxx_reg_lock(p->chip); ++ err = mv88e6xxx_led_hw_control_set(p, 1, rules); ++ mv88e6xxx_reg_unlock(p->chip); ++ ++ return err; ++} ++ ++static int ++mv88e6xxx_led0_hw_control_get(struct led_classdev *ldev, unsigned long *rules) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0); ++ ++ return mv88e6xxx_led_hw_control_get(p, 0, rules); ++} ++ ++static int ++mv88e6xxx_led1_hw_control_get(struct led_classdev *ldev, unsigned long *rules) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1); ++ ++ return mv88e6xxx_led_hw_control_get(p, 1, rules); ++} ++ ++static struct device *mv88e6xxx_led_hw_control_get_device(struct mv88e6xxx_port *p) ++{ ++ struct dsa_port *dp; ++ ++ dp = dsa_to_port(p->chip->ds, p->port); ++ if (!dp) ++ return NULL; ++ if (dp->user) ++ return &dp->user->dev; ++ return NULL; ++} ++ ++static struct device * ++mv88e6xxx_led0_hw_control_get_device(struct led_classdev *ldev) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0); ++ ++ return mv88e6xxx_led_hw_control_get_device(p); ++} ++ ++static struct device * ++mv88e6xxx_led1_hw_control_get_device(struct led_classdev *ldev) ++{ ++ struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1); ++ ++ return mv88e6xxx_led_hw_control_get_device(p); ++} ++ ++int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip, int port) ++{ ++ struct fwnode_handle *led = NULL, *leds = NULL; ++ struct led_init_data init_data = { }; ++ enum led_default_state state; ++ struct mv88e6xxx_port *p; ++ struct led_classdev *l; ++ struct device *dev; ++ u32 led_num; ++ int ret; ++ ++ /* LEDs are on ports 1,2,3,4, 5 and 6 (index 0..5), no more */ ++ if (port > 5) ++ return -EOPNOTSUPP; ++ ++ p = &chip->ports[port]; ++ if (!p->fwnode) ++ return 0; ++ ++ dev = chip->dev; ++ ++ leds = fwnode_get_named_child_node(p->fwnode, "leds"); ++ if (!leds) { ++ dev_dbg(dev, "No Leds node specified in device tree for port %d!\n", ++ port); ++ return 0; ++ } ++ ++ fwnode_for_each_child_node(leds, led) { ++ /* Reg represent the led number of the port, max 2 ++ * LEDs can be connected to each port, in some designs ++ * only one LED is connected. ++ */ ++ if (fwnode_property_read_u32(led, "reg", &led_num)) ++ continue; ++ if (led_num > 1) { ++ dev_err(dev, "invalid LED specified port %d\n", port); ++ return -EINVAL; ++ } ++ ++ if (led_num == 0) ++ l = &p->led0; ++ else ++ l = &p->led1; ++ ++ state = led_init_default_state_get(led); ++ switch (state) { ++ case LEDS_DEFSTATE_ON: ++ l->brightness = 1; ++ mv88e6xxx_led_brightness_set(p, led_num, 1); ++ break; ++ case LEDS_DEFSTATE_KEEP: ++ break; ++ default: ++ l->brightness = 0; ++ mv88e6xxx_led_brightness_set(p, led_num, 0); ++ } ++ ++ l->max_brightness = 1; ++ if (led_num == 0) { ++ l->brightness_set_blocking = mv88e6xxx_led0_brightness_set_blocking; ++ l->blink_set = mv88e6xxx_led0_blink_set; ++ l->hw_control_is_supported = mv88e6xxx_led0_hw_control_is_supported; ++ l->hw_control_set = mv88e6xxx_led0_hw_control_set; ++ l->hw_control_get = mv88e6xxx_led0_hw_control_get; ++ l->hw_control_get_device = mv88e6xxx_led0_hw_control_get_device; ++ } else { ++ l->brightness_set_blocking = mv88e6xxx_led1_brightness_set_blocking; ++ l->blink_set = mv88e6xxx_led1_blink_set; ++ l->hw_control_is_supported = mv88e6xxx_led1_hw_control_is_supported; ++ l->hw_control_set = mv88e6xxx_led1_hw_control_set; ++ l->hw_control_get = mv88e6xxx_led1_hw_control_get; ++ l->hw_control_get_device = mv88e6xxx_led1_hw_control_get_device; ++ } ++ l->hw_control_trigger = "netdev"; ++ ++ init_data.default_label = ":port"; ++ init_data.fwnode = led; ++ init_data.devname_mandatory = true; ++ init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d:0%d", chip->info->name, ++ port, led_num); ++ if (!init_data.devicename) ++ return -ENOMEM; ++ ++ ret = devm_led_classdev_register_ext(dev, l, &init_data); ++ kfree(init_data.devicename); ++ ++ if (ret) { ++ dev_err(dev, "Failed to init LED %d for port %d", led_num, port); ++ return ret; ++ } ++ } ++ ++ return 0; ++} +--- a/drivers/net/dsa/mv88e6xxx/port.c ++++ b/drivers/net/dsa/mv88e6xxx/port.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #include "chip.h" + #include "global2.h" +--- a/drivers/net/dsa/mv88e6xxx/port.h ++++ b/drivers/net/dsa/mv88e6xxx/port.h +@@ -309,6 +309,130 @@ + /* Offset 0x13: OutFiltered Counter */ + #define MV88E6XXX_PORT_OUT_FILTERED 0x13 + ++/* Offset 0x16: LED Control */ ++#define MV88E6XXX_PORT_LED_CONTROL 0x16 ++#define MV88E6XXX_PORT_LED_CONTROL_UPDATE BIT(15) ++#define MV88E6XXX_PORT_LED_CONTROL_POINTER_MASK GENMASK(14, 12) ++#define MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL (0x00 << 12) /* Control for LED 0 and 1 */ ++#define MV88E6XXX_PORT_LED_CONTROL_POINTER_STRETCH_BLINK (0x06 << 12) /* Stetch and Blink Rate */ ++#define MV88E6XXX_PORT_LED_CONTROL_POINTER_CNTL_SPECIAL (0x07 << 12) /* Control for the Port's Special LED */ ++#define MV88E6XXX_PORT_LED_CONTROL_DATA_MASK GENMASK(10, 0) ++/* Selection masks valid for either port 1,2,3,4 or 5 */ ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK GENMASK(3, 0) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK GENMASK(7, 4) ++/* Selection control for LED 0 and 1, ports 5 and 6 only has LED 0 ++ * Bits Function ++ * 0..3 LED 0 control selector on ports 1-5 ++ * 4..7 LED 1 control selector on ports 1-4 on port 5 this controls LED 0 of port 6 ++ * ++ * Sel Port LED Function for the 6352 family: ++ * 0 1-4 0 Link/Act/Speed by Blink Rate (off=no link, on=link, blink=activity, blink speed=link speed) ++ * 1-4 1 Port 2's Special LED ++ * 5-6 0 Port 5 Link/Act (off=no link, on=link, blink=activity) ++ * 5-6 1 Port 6 Link/Act (off=no link, on=link 1000, blink=activity) ++ * 1 1-4 0 100/1000 Link/Act (off=no link, on=100 or 1000 link, blink=activity) ++ * 1-4 1 10/100 Link Act (off=no link, on=10 or 100 link, blink=activity) ++ * 5-6 0 Fiber 100 Link/Act (off=no link, on=link 100, blink=activity) ++ * 5-6 1 Fiber 1000 Link/Act (off=no link, on=link 1000, blink=activity) ++ * 2 1-4 0 1000 Link/Act (off=no link, on=link 1000, blink=activity) ++ * 1-4 1 10/100 Link/Act (off=no link, on=10 or 100 link, blink=activity) ++ * 5-6 0 Fiber 1000 Link/Act (off=no link, on=link 1000, blink=activity) ++ * 5-6 1 Fiber 100 Link/Act (off=no link, on=link 100, blink=activity) ++ * 3 1-4 0 Link/Act (off=no link, on=link, blink=activity) ++ * 1-4 1 1000 Link (off=no link, on=1000 link) ++ * 5-6 0 Port 0's Special LED ++ * 5-6 1 Fiber Link (off=no link, on=link) ++ * 4 1-4 0 Port 0's Special LED ++ * 1-4 1 Port 1's Special LED ++ * 5-6 0 Port 1's Special LED ++ * 5-6 1 Port 5 Link/Act (off=no link, on=link, blink=activity) ++ * 5 1-4 0 Reserved ++ * 1-4 1 Reserved ++ * 5-6 0 Port 2's Special LED ++ * 5-6 1 Port 6 Link (off=no link, on=link) ++ * 6 1-4 0 Duplex/Collision (off=half-duplex,on=full-duplex,blink=collision) ++ * 1-4 1 10/1000 Link/Act (off=no link, on=10 or 1000 link, blink=activity) ++ * 5-6 0 Port 5 Duplex/Collision (off=half-duplex, on=full-duplex, blink=col) ++ * 5-6 1 Port 6 Duplex/Collision (off=half-duplex, on=full-duplex, blink=col) ++ * 7 1-4 0 10/1000 Link/Act (off=no link, on=10 or 1000 link, blink=activity) ++ * 1-4 1 10/1000 Link (off=no link, on=10 or 1000 link) ++ * 5-6 0 Port 5 Link/Act/Speed by Blink rate (off=no link, on=link, blink=activity, blink speed=link speed) ++ * 5-6 1 Port 6 Link/Act/Speed by Blink rate (off=no link, on=link, blink=activity, blink speed=link speed) ++ * 8 1-4 0 Link (off=no link, on=link) ++ * 1-4 1 Activity (off=no link, blink on=activity) ++ * 5-6 0 Port 6 Link/Act (off=no link, on=link, blink=activity) ++ * 5-6 1 Port 0's Special LED ++ * 9 1-4 0 10 Link (off=no link, on=10 link) ++ * 1-4 1 100 Link (off=no link, on=100 link) ++ * 5-6 0 Reserved ++ * 5-6 1 Port 1's Special LED ++ * a 1-4 0 10 Link/Act (off=no link, on=10 link, blink=activity) ++ * 1-4 1 100 Link/Act (off=no link, on=100 link, blink=activity) ++ * 5-6 0 Reserved ++ * 5-6 1 Port 2's Special LED ++ * b 1-4 0 100/1000 Link (off=no link, on=100 or 1000 link) ++ * 1-4 1 10/100 Link (off=no link, on=100 link, blink=activity) ++ * 5-6 0 Reserved ++ * 5-6 1 Reserved ++ * c * * PTP Act (blink on=PTP activity) ++ * d * * Force Blink ++ * e * * Force Off ++ * f * * Force On ++ */ ++/* Select LED0 output */ ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL0 0x0 ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1 0x1 ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2 0x2 ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL3 0x3 ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL4 0x4 ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL5 0x5 ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6 0x6 ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL7 0x7 ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8 0x8 ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL9 0x9 ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELA 0xa ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELB 0xb ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELC 0xc ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELD 0xd ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELE 0xe ++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELF 0xf ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL0 (0x0 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1 (0x1 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2 (0x2 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3 (0x3 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL4 (0x4 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL5 (0x5 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6 (0x6 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL7 (0x7 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL8 (0x8 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL9 (0x9 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELA (0xa << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELB (0xb << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELC (0xc << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELD (0xd << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELE (0xe << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELF (0xf << 4) ++/* Stretch and Blink Rate Control (Index 0x06 of LED Control) */ ++/* Pulse Stretch Selection for all LED's on this port */ ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_NONE (0 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS (1 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_42MS (2 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_84MS (3 << 4) ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_168MS (4 << 4) ++/* Blink Rate Selection for all LEDs on this port */ ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_21MS 0 ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_42MS 1 ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_84MS 2 ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_168MS 3 ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_336MS 4 ++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_672MS 5 ++ /* Control for Special LED (Index 0x7 of LED Control on Port0) */ ++#define MV88E6XXX_PORT_LED_CONTROL_0x07_P0_LAN_LINKACT_SHIFT 0 /* bits 6:0 LAN Link Activity LED */ ++/* Control for Special LED (Index 0x7 of LED Control on Port 1) */ ++#define MV88E6XXX_PORT_LED_CONTROL_0x07_P1_WAN_LINKACT_SHIFT 0 /* bits 6:0 WAN Link Activity LED */ ++/* Control for Special LED (Index 0x7 of LED Control on Port 2) */ ++#define MV88E6XXX_PORT_LED_CONTROL_0x07_P2_PTP_ACT 0 /* bits 6:0 PTP Activity */ ++ + /* Offset 0x18: IEEE Priority Mapping Table */ + #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE 0x18 + #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE 0x8000 +@@ -457,6 +581,15 @@ int mv88e6393x_port_set_cmode(struct mv8 + phy_interface_t mode); + int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); + int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); ++#ifdef CONFIG_NET_DSA_MV88E6XXX_LEDS ++int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip, int port); ++#else ++static inline int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip, ++ int port) ++{ ++ return 0; ++} ++#endif + int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port, + bool drop_untagged); + int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port, bool map); diff --git a/target/linux/generic/backport-6.12/741-v6.13-net-dsa-mv88e6xxx-fix-unreleased-fwnode_handle-in-se.patch b/target/linux/generic/backport-6.12/741-v6.13-net-dsa-mv88e6xxx-fix-unreleased-fwnode_handle-in-se.patch new file mode 100644 index 00000000000..2c81983a209 --- /dev/null +++ b/target/linux/generic/backport-6.12/741-v6.13-net-dsa-mv88e6xxx-fix-unreleased-fwnode_handle-in-se.patch @@ -0,0 +1,31 @@ +From b8ee7a11c75436b85fa1641aa5f970de0f8a575c Mon Sep 17 00:00:00 2001 +From: Javier Carrasco +Date: Sat, 19 Oct 2024 22:16:49 +0200 +Subject: net: dsa: mv88e6xxx: fix unreleased fwnode_handle in setup_port() + +'ports_fwnode' is initialized via device_get_named_child_node(), which +requires a call to fwnode_handle_put() when the variable is no longer +required to avoid leaking memory. + +Add the missing fwnode_handle_put() after 'ports_fwnode' has been used +and is no longer required. + +Fixes: 94a2a84f5e9e ("net: dsa: mv88e6xxx: Support LED control") +Signed-off-by: Javier Carrasco +Reviewed-by: Andrew Lunn +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mv88e6xxx/chip.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -3441,6 +3441,7 @@ static int mv88e6xxx_setup_port(struct m + break; + } + } ++ fwnode_handle_put(ports_fwnode); + } else { + dev_dbg(chip->dev, "no ethernet ports node defined for the device\n"); + } diff --git a/target/linux/generic/backport-6.12/742-v6.17-net-dsa-mv88e6xxx-Fix-fwnode-reference-leaks-in-mv88.patch b/target/linux/generic/backport-6.12/742-v6.17-net-dsa-mv88e6xxx-Fix-fwnode-reference-leaks-in-mv88.patch new file mode 100644 index 00000000000..6e0fbb82afe --- /dev/null +++ b/target/linux/generic/backport-6.12/742-v6.17-net-dsa-mv88e6xxx-Fix-fwnode-reference-leaks-in-mv88.patch @@ -0,0 +1,68 @@ +From f63e7c8a83892781f6ceb55566f9497639c44555 Mon Sep 17 00:00:00 2001 +From: Miaoqian Lin +Date: Mon, 1 Sep 2025 15:32:23 +0800 +Subject: net: dsa: mv88e6xxx: Fix fwnode reference leaks in + mv88e6xxx_port_setup_leds + +Fix multiple fwnode reference leaks: + +1. The function calls fwnode_get_named_child_node() to get the "leds" node, + but never calls fwnode_handle_put(leds) to release this reference. + +2. Within the fwnode_for_each_child_node() loop, the early return + paths that don't properly release the "led" fwnode reference. + +This fix follows the same pattern as commit d029edefed39 +("net dsa: qca8k: fix usages of device_get_named_child_node()") + +Fixes: 94a2a84f5e9e ("net: dsa: mv88e6xxx: Support LED control") +Cc: stable@vger.kernel.org +Signed-off-by: Miaoqian Lin +Reviewed-by: Linus Walleij +Link: https://patch.msgid.link/20250901073224.2273103-1-linmq006@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mv88e6xxx/leds.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/mv88e6xxx/leds.c ++++ b/drivers/net/dsa/mv88e6xxx/leds.c +@@ -779,7 +779,8 @@ int mv88e6xxx_port_setup_leds(struct mv8 + continue; + if (led_num > 1) { + dev_err(dev, "invalid LED specified port %d\n", port); +- return -EINVAL; ++ ret = -EINVAL; ++ goto err_put_led; + } + + if (led_num == 0) +@@ -823,17 +824,25 @@ int mv88e6xxx_port_setup_leds(struct mv8 + init_data.devname_mandatory = true; + init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d:0%d", chip->info->name, + port, led_num); +- if (!init_data.devicename) +- return -ENOMEM; ++ if (!init_data.devicename) { ++ ret = -ENOMEM; ++ goto err_put_led; ++ } + + ret = devm_led_classdev_register_ext(dev, l, &init_data); + kfree(init_data.devicename); + + if (ret) { + dev_err(dev, "Failed to init LED %d for port %d", led_num, port); +- return ret; ++ goto err_put_led; + } + } + ++ fwnode_handle_put(leds); + return 0; ++ ++err_put_led: ++ fwnode_handle_put(led); ++ fwnode_handle_put(leds); ++ return ret; + } diff --git a/target/linux/generic/backport-6.12/750-v7.0-net-phy-move-mmd_phy_read-and-mmd_phy_write-to-phyli.patch b/target/linux/generic/backport-6.12/750-v7.0-net-phy-move-mmd_phy_read-and-mmd_phy_write-to-phyli.patch new file mode 100644 index 00000000000..50c91557827 --- /dev/null +++ b/target/linux/generic/backport-6.12/750-v7.0-net-phy-move-mmd_phy_read-and-mmd_phy_write-to-phyli.patch @@ -0,0 +1,65 @@ +From 65de36f5eae1c540115bf8f705f853718e9b6451 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 5 Jan 2026 16:38:29 +0000 +Subject: [PATCH 3/5] net: phy: move mmd_phy_read and mmd_phy_write to phylib.h + +Helper functions mmd_phy_read and mmd_phy_write are useful for PHYs +which require custom MMD access functions for some but not all MMDs. +Move mmd_phy_read and mmd_phy_write function prototypes from +phylib-internal.h to phylib.h to make them available for PHY drivers. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/79169cd624a3572d426e42c7b13cd2654a35d0cb.1767630451.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni + +Note that this patch replaces the commit above as neither phylink.h nor +phylink-internal.h exist in Linux 6.12. +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -542,8 +542,8 @@ static void mmd_phy_indirect(struct mii_ + devad | MII_MMD_CTRL_NOINCR); + } + +-static int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45, +- int devad, u32 regnum) ++int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45, ++ int devad, u32 regnum) + { + if (is_c45) + return __mdiobus_c45_read(bus, phy_addr, devad, regnum); +@@ -552,9 +552,10 @@ static int mmd_phy_read(struct mii_bus * + /* Read the content of the MMD's selected register */ + return __mdiobus_read(bus, phy_addr, MII_MMD_DATA); + } ++EXPORT_SYMBOL_GPL(mmd_phy_read); + +-static int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45, +- int devad, u32 regnum, u16 val) ++int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45, ++ int devad, u32 regnum, u16 val) + { + if (is_c45) + return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val); +@@ -563,6 +564,7 @@ static int mmd_phy_write(struct mii_bus + /* Write the data into MMD's selected register */ + return __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); + } ++EXPORT_SYMBOL_GPL(mmd_phy_write); + + /** + * __phy_read_mmd - Convenience function for reading a register +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -2032,6 +2032,11 @@ extern struct phy_driver genphy_c45_driv + /* The gen10g_* functions are the old Clause 45 stub */ + int gen10g_config_aneg(struct phy_device *phydev); + ++int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45, ++ int devad, u32 regnum); ++int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45, ++ int devad, u32 regnum, u16 val); ++ + static inline int phy_read_status(struct phy_device *phydev) + { + if (!phydev->drv) diff --git a/target/linux/generic/backport-6.12/760-v6.18-net-phy-introduce-phy_id_compare_model-PHY-ID-helper.patch b/target/linux/generic/backport-6.12/760-v6.18-net-phy-introduce-phy_id_compare_model-PHY-ID-helper.patch new file mode 100644 index 00000000000..a367f66504d --- /dev/null +++ b/target/linux/generic/backport-6.12/760-v6.18-net-phy-introduce-phy_id_compare_model-PHY-ID-helper.patch @@ -0,0 +1,39 @@ +From ae1c658b33d4bec20c037aebba583a68375d4773 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 11 Sep 2025 15:08:31 +0200 +Subject: [PATCH] net: phy: introduce phy_id_compare_model() PHY ID helper + +Similar to phy_id_compare_vendor(), introduce the equivalent +phy_id_compare_model() helper for the generic PHY ID Model mask. + +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250911130840.23569-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + include/linux/phy.h | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1284,6 +1284,19 @@ static inline bool phy_id_compare(u32 id + } + + /** ++ * phy_id_compare_model - compare @id with @model mask ++ * @id: PHY ID ++ * @model_mask: PHY Model mask ++ * ++ * Return: true if the bits from @id match @model using the ++ * generic PHY Model mask. ++ */ ++static inline bool phy_id_compare_model(u32 id, u32 model_mask) ++{ ++ return phy_id_compare(id, model_mask, PHY_ID_MATCH_MODEL_MASK); ++} ++ ++/** + * phydev_id_compare - compare @id with the PHY's Clause 22 ID + * @phydev: the PHY device + * @id: the PHY ID to be matched diff --git a/target/linux/generic/backport-6.12/761-v6.19-net-phy-mxl-gpy-add-support-for-mxl86252-and-mxl86282.patch b/target/linux/generic/backport-6.12/761-v6.19-net-phy-mxl-gpy-add-support-for-mxl86252-and-mxl86282.patch new file mode 100644 index 00000000000..87b3e3623d2 --- /dev/null +++ b/target/linux/generic/backport-6.12/761-v6.19-net-phy-mxl-gpy-add-support-for-mxl86252-and-mxl86282.patch @@ -0,0 +1,163 @@ +From de1e5c9333f426348571f7a3b034f99490d3f926 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 22 Nov 2025 13:33:47 +0000 +Subject: [PATCH] net: phy: mxl-gpy: add support for MxL86252 and MxL86282 + +Add PHY driver support for Maxlinear MxL86252 and MxL86282 switches. +The PHYs built-into those switches are just like any other GPY 2.5G PHYs +with the exception of the temperature sensor data being encoded in a +different way. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/a6cd7fe461b011cec2b59dffaf34e9c8b0819059.1763818120.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mxl-gpy.c | 91 ++++++++++++++++++++++++++++++++++++++- + 1 file changed, 89 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -31,6 +31,8 @@ + #define PHY_ID_GPY241BM 0x67C9DE80 + #define PHY_ID_GPY245B 0x67C9DEC0 + #define PHY_ID_MXL86211C 0xC1335400 ++#define PHY_ID_MXL86252 0xC1335520 ++#define PHY_ID_MXL86282 0xC1335500 + + #define PHY_CTL1 0x13 + #define PHY_CTL1_MDICD BIT(3) +@@ -200,6 +202,29 @@ static int gpy_hwmon_read(struct device + return 0; + } + ++static int mxl862x2_hwmon_read(struct device *dev, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel, long *value) ++{ ++ struct phy_device *phydev = dev_get_drvdata(dev); ++ long tmp; ++ int ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_TEMP_STA); ++ if (ret < 0) ++ return ret; ++ if (!ret) ++ return -ENODATA; ++ ++ tmp = (s16)ret; ++ tmp *= 78125; ++ tmp /= 10000; ++ ++ *value = tmp; ++ ++ return 0; ++} ++ + static umode_t gpy_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +@@ -217,14 +242,25 @@ static const struct hwmon_ops gpy_hwmon_ + .read = gpy_hwmon_read, + }; + ++static const struct hwmon_ops mxl862x2_hwmon_hwmon_ops = { ++ .is_visible = gpy_hwmon_is_visible, ++ .read = mxl862x2_hwmon_read, ++}; ++ + static const struct hwmon_chip_info gpy_hwmon_chip_info = { + .ops = &gpy_hwmon_hwmon_ops, + .info = gpy_hwmon_info, + }; + ++static const struct hwmon_chip_info mxl862x2_hwmon_chip_info = { ++ .ops = &mxl862x2_hwmon_hwmon_ops, ++ .info = gpy_hwmon_info, ++}; ++ + static int gpy_hwmon_register(struct phy_device *phydev) + { + struct device *dev = &phydev->mdio.dev; ++ const struct hwmon_chip_info *info; + struct device *hwmon_dev; + char *hwmon_name; + +@@ -232,10 +268,15 @@ static int gpy_hwmon_register(struct phy + if (IS_ERR(hwmon_name)) + return PTR_ERR(hwmon_name); + ++ if (phy_id_compare_model(phydev->phy_id, PHY_ID_MXL86252) || ++ phy_id_compare_model(phydev->phy_id, PHY_ID_MXL86282)) ++ info = &mxl862x2_hwmon_chip_info; ++ else ++ info = &gpy_hwmon_chip_info; ++ + hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name, + phydev, +- &gpy_hwmon_chip_info, +- NULL); ++ info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); + } +@@ -1331,6 +1372,50 @@ static struct phy_driver gpy_drivers[] = + .led_hw_control_set = gpy_led_hw_control_set, + .led_polarity_set = gpy_led_polarity_set, + }, ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_MXL86252), ++ .name = "MaxLinear Ethernet MxL86252", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_MODEL(PHY_ID_MXL86282), ++ .name = "MaxLinear Ethernet MxL86282", ++ .get_features = genphy_c45_pma_read_abilities, ++ .config_init = gpy_config_init, ++ .probe = gpy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .config_aneg = gpy_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = gpy_read_status, ++ .config_intr = gpy_config_intr, ++ .handle_interrupt = gpy_handle_interrupt, ++ .set_wol = gpy_set_wol, ++ .get_wol = gpy_get_wol, ++ .set_loopback = gpy_loopback, ++ .led_brightness_set = gpy_led_brightness_set, ++ .led_hw_is_supported = gpy_led_hw_is_supported, ++ .led_hw_control_get = gpy_led_hw_control_get, ++ .led_hw_control_set = gpy_led_hw_control_set, ++ .led_polarity_set = gpy_led_polarity_set, ++ }, + }; + module_phy_driver(gpy_drivers); + +@@ -1348,6 +1433,8 @@ static const struct mdio_device_id __may + {PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)}, + {PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)}, + {PHY_ID_MATCH_MODEL(PHY_ID_MXL86211C)}, ++ {PHY_ID_MATCH_MODEL(PHY_ID_MXL86252)}, ++ {PHY_ID_MATCH_MODEL(PHY_ID_MXL86282)}, + { } + }; + MODULE_DEVICE_TABLE(mdio, gpy_tbl); diff --git a/target/linux/generic/backport-6.12/762-v7.0-net-dsa-add-tag-format-for-MxL862xx-switches.patch b/target/linux/generic/backport-6.12/762-v7.0-net-dsa-add-tag-format-for-MxL862xx-switches.patch new file mode 100644 index 00000000000..94fb211a070 --- /dev/null +++ b/target/linux/generic/backport-6.12/762-v7.0-net-dsa-add-tag-format-for-MxL862xx-switches.patch @@ -0,0 +1,191 @@ +From 1ecc2ebd1298d5c0eaa238e71b7d2109d7d77538 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 7 Feb 2026 03:07:11 +0000 +Subject: [PATCH 01/35] net: dsa: add tag format for MxL862xx switches + +Add proprietary special tag format for the MaxLinear MXL862xx family of +switches. While using the same Ethertype as MaxLinear's GSW1xx switches, +the actual tag format differs significantly, hence we need a dedicated +tag driver for that. + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/c64e6ddb6c93a4fac39f9ab9b2d8bf551a2b118d.1770433307.git.daniel@makrotopia.org +Reviewed-by: Vladimir Oltean +Signed-off-by: Paolo Abeni +--- + include/net/dsa.h | 2 + + net/dsa/Kconfig | 7 +++ + net/dsa/Makefile | 1 + + net/dsa/tag_mxl862xx.c | 110 +++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 120 insertions(+) + create mode 100644 net/dsa/tag_mxl862xx.c + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -55,6 +55,7 @@ struct tc_action; + #define DSA_TAG_PROTO_LAN937X_VALUE 27 + #define DSA_TAG_PROTO_VSC73XX_8021Q_VALUE 28 + #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29 ++#define DSA_TAG_PROTO_MXL862_VALUE 30 + + enum dsa_tag_protocol { + DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, +@@ -87,6 +88,7 @@ enum dsa_tag_protocol { + DSA_TAG_PROTO_RZN1_A5PSW = DSA_TAG_PROTO_RZN1_A5PSW_VALUE, + DSA_TAG_PROTO_LAN937X = DSA_TAG_PROTO_LAN937X_VALUE, + DSA_TAG_PROTO_VSC73XX_8021Q = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE, ++ DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE, + }; + + struct dsa_switch; +--- a/net/dsa/Kconfig ++++ b/net/dsa/Kconfig +@@ -104,6 +104,13 @@ config NET_DSA_TAG_MTK + Say Y or M if you want to enable support for tagging frames for + Mediatek switches. + ++config NET_DSA_TAG_MXL_862XX ++ tristate "Tag driver for MaxLinear MxL862xx switches" ++ help ++ Say Y or M if you want to enable support for tagging frames for the ++ MaxLinear MxL86252 and MxL86282 switches using their native 8-byte ++ tagging protocol. ++ + config NET_DSA_TAG_KSZ + tristate "Tag driver for Microchip 8795/937x/9477/9893 families of switches" + help +--- a/net/dsa/Makefile ++++ b/net/dsa/Makefile +@@ -28,6 +28,7 @@ obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += t + obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o + obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o + obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o ++obj-$(CONFIG_NET_DSA_TAG_MXL_862XX) += tag_mxl862xx.o + obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o + obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o + obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o +--- /dev/null ++++ b/net/dsa/tag_mxl862xx.c +@@ -0,0 +1,110 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * DSA Special Tag for MaxLinear 862xx switch chips ++ * ++ * Copyright (C) 2025 Daniel Golle ++ * Copyright (C) 2024 MaxLinear Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "tag.h" ++ ++#define MXL862_NAME "mxl862xx" ++ ++#define MXL862_HEADER_LEN 8 ++ ++/* Word 0 -> EtherType */ ++ ++/* Word 2 */ ++#define MXL862_SUBIF_ID GENMASK(4, 0) ++ ++/* Word 3 */ ++#define MXL862_IGP_EGP GENMASK(3, 0) ++ ++static struct sk_buff *mxl862_tag_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct dsa_port *dp = dsa_user_to_port(dev); ++ struct dsa_port *cpu_dp = dp->cpu_dp; ++ unsigned int cpu_port, sub_interface; ++ __be16 *mxl862_tag; ++ ++ cpu_port = cpu_dp->index; ++ ++ /* target port sub-interface ID relative to the CPU port */ ++ sub_interface = dp->index + 16 - cpu_port; ++ ++ /* provide additional space 'MXL862_HEADER_LEN' bytes */ ++ skb_push(skb, MXL862_HEADER_LEN); ++ ++ /* shift MAC address to the beginning of the enlarged buffer, ++ * releasing the space required for DSA tag (between MAC address and ++ * Ethertype) ++ */ ++ dsa_alloc_etype_header(skb, MXL862_HEADER_LEN); ++ ++ /* special tag ingress (from the perspective of the switch) */ ++ mxl862_tag = dsa_etype_header_pos_tx(skb); ++ mxl862_tag[0] = htons(ETH_P_MXLGSW); ++ mxl862_tag[1] = 0; ++ mxl862_tag[2] = htons(FIELD_PREP(MXL862_SUBIF_ID, sub_interface)); ++ mxl862_tag[3] = htons(FIELD_PREP(MXL862_IGP_EGP, cpu_port)); ++ ++ return skb; ++} ++ ++static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ __be16 *mxl862_tag; ++ int port; ++ ++ if (unlikely(!pskb_may_pull(skb, MXL862_HEADER_LEN))) { ++ dev_warn_ratelimited(&dev->dev, "Cannot pull SKB, packet dropped\n"); ++ return NULL; ++ } ++ ++ mxl862_tag = dsa_etype_header_pos_rx(skb); ++ ++ if (unlikely(mxl862_tag[0] != htons(ETH_P_MXLGSW))) { ++ dev_warn_ratelimited(&dev->dev, ++ "Invalid special tag marker, packet dropped, tag: %8ph\n", ++ mxl862_tag); ++ return NULL; ++ } ++ ++ /* Get source port information */ ++ port = FIELD_GET(MXL862_IGP_EGP, ntohs(mxl862_tag[3])); ++ skb->dev = dsa_conduit_find_user(dev, 0, port); ++ if (unlikely(!skb->dev)) { ++ dev_warn_ratelimited(&dev->dev, ++ "Invalid source port, packet dropped, tag: %8ph\n", ++ mxl862_tag); ++ return NULL; ++ } ++ ++ /* remove the MxL862xx special tag between the MAC addresses and the ++ * current ethertype field. ++ */ ++ skb_pull_rcsum(skb, MXL862_HEADER_LEN); ++ dsa_strip_etype_header(skb, MXL862_HEADER_LEN); ++ ++ return skb; ++} ++ ++static const struct dsa_device_ops mxl862_netdev_ops = { ++ .name = MXL862_NAME, ++ .proto = DSA_TAG_PROTO_MXL862, ++ .xmit = mxl862_tag_xmit, ++ .rcv = mxl862_tag_rcv, ++ .needed_headroom = MXL862_HEADER_LEN, ++}; ++ ++MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862, MXL862_NAME); ++MODULE_DESCRIPTION("DSA tag driver for MaxLinear MxL862xx switches"); ++MODULE_LICENSE("GPL"); ++ ++module_dsa_tag_driver(mxl862_netdev_ops); +--- a/include/uapi/linux/if_ether.h ++++ b/include/uapi/linux/if_ether.h +@@ -92,6 +92,9 @@ + #define ETH_P_ETHERCAT 0x88A4 /* EtherCAT */ + #define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ + #define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ ++#define ETH_P_MXLGSW 0x88C3 /* Infineon Technologies Corporate Research ST ++ * Used by MaxLinear GSW DSA ++ */ + #define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */ + #define ETH_P_TIPC 0x88CA /* TIPC */ + #define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */ diff --git a/target/linux/generic/backport-6.12/763-v7.0-net-mdio-add-unlocked-mdiodev-C45-bus-accessors.patch b/target/linux/generic/backport-6.12/763-v7.0-net-mdio-add-unlocked-mdiodev-C45-bus-accessors.patch new file mode 100644 index 00000000000..e05dd1fe3ca --- /dev/null +++ b/target/linux/generic/backport-6.12/763-v7.0-net-mdio-add-unlocked-mdiodev-C45-bus-accessors.patch @@ -0,0 +1,41 @@ +From 1111454d5a637e039a46b867088b524c73159da4 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 7 Feb 2026 03:07:18 +0000 +Subject: [PATCH 02/35] net: mdio: add unlocked mdiodev C45 bus accessors + +Add helper inline functions __mdiodev_c45_read() and +__mdiodev_c45_write(), which are the C45 equivalents of the existing +__mdiodev_read() and __mdiodev_write() added by commit e6a45700e7e1 +("net: mdio: add unlocked mdiobus and mdiodev bus accessors") + +Signed-off-by: Daniel Golle +Reviewed-by: Russell King (Oracle) +Link: https://patch.msgid.link/8d1d55949a75a871d2a3b90e421de4bd58d77685.1770433307.git.daniel@makrotopia.org +Reviewed-by: Vladimir Oltean +Signed-off-by: Paolo Abeni +--- + include/linux/mdio.h | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/include/linux/mdio.h ++++ b/include/linux/mdio.h +@@ -668,6 +668,19 @@ static inline int mdiodev_modify_changed + mask, set); + } + ++static inline int __mdiodev_c45_read(struct mdio_device *mdiodev, int devad, ++ u16 regnum) ++{ ++ return __mdiobus_c45_read(mdiodev->bus, mdiodev->addr, devad, regnum); ++} ++ ++static inline int __mdiodev_c45_write(struct mdio_device *mdiodev, u32 devad, ++ u16 regnum, u16 val) ++{ ++ return __mdiobus_c45_write(mdiodev->bus, mdiodev->addr, devad, regnum, ++ val); ++} ++ + static inline int mdiodev_c45_modify(struct mdio_device *mdiodev, int devad, + u32 regnum, u16 mask, u16 set) + { diff --git a/target/linux/generic/backport-6.12/764-v7.0-net-dsa-add-basic-initial-driver-for-MxL862xx-switch.patch b/target/linux/generic/backport-6.12/764-v7.0-net-dsa-add-basic-initial-driver-for-MxL862xx-switch.patch new file mode 100644 index 00000000000..247c6981dab --- /dev/null +++ b/target/linux/generic/backport-6.12/764-v7.0-net-dsa-add-basic-initial-driver-for-MxL862xx-switch.patch @@ -0,0 +1,1583 @@ +From c764b51397f5f5919b07fdfbb9b70082059e1c16 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 7 Feb 2026 03:07:27 +0000 +Subject: [PATCH 03/35] net: dsa: add basic initial driver for MxL862xx + switches + +Add very basic DSA driver for MaxLinear's MxL862xx switches. + +In contrast to previous MaxLinear switches the MxL862xx has a built-in +processor that runs a sophisticated firmware based on Zephyr RTOS. +Interaction between the host and the switch hence is organized using a +software API of that firmware rather than accessing hardware registers +directly. + +Add descriptions of the most basic firmware API calls to access the +built-in MDIO bus hosting the 2.5GE PHYs, basic port control as well as +setting up the CPU port. + +Implement a very basic DSA driver using that API which is sufficient to +get packets flowing between the user ports and the CPU port. + +The firmware offers all features one would expect from a modern switch +hardware, they are going to be added one by one in follow-up patch +series. + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/ccde07e8cf33d8ae243000013b57cfaa2695e0a9.1770433307.git.daniel@makrotopia.org +Reviewed-by: Vladimir Oltean +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/Kconfig | 2 + + drivers/net/dsa/Makefile | 1 + + drivers/net/dsa/mxl862xx/Kconfig | 12 + + drivers/net/dsa/mxl862xx/Makefile | 3 + + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 675 +++++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 49 ++ + drivers/net/dsa/mxl862xx/mxl862xx-host.c | 245 ++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-host.h | 12 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 476 ++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx.h | 16 + + 10 files changed, 1491 insertions(+) + create mode 100644 drivers/net/dsa/mxl862xx/Kconfig + create mode 100644 drivers/net/dsa/mxl862xx/Makefile + create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-api.h + create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-cmd.h + create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-host.c + create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-host.h + create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx.c + create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx.h + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -79,6 +79,8 @@ source "drivers/net/dsa/microchip/Kconfi + + source "drivers/net/dsa/mv88e6xxx/Kconfig" + ++source "drivers/net/dsa/mxl862xx/Kconfig" ++ + source "drivers/net/dsa/ocelot/Kconfig" + + source "drivers/net/dsa/qca/Kconfig" +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -21,6 +21,7 @@ obj-y += b53/ + obj-y += hirschmann/ + obj-y += microchip/ + obj-y += mv88e6xxx/ ++obj-y += mxl862xx/ + obj-y += ocelot/ + obj-y += qca/ + obj-y += realtek/ +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/Kconfig +@@ -0,0 +1,12 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++config NET_DSA_MXL862 ++ tristate "MaxLinear MxL862xx" ++ depends on NET_DSA ++ select MAXLINEAR_GPHY ++ select NET_DSA_TAG_MXL_862XX ++ help ++ This enables support for the MaxLinear MxL862xx switch family. ++ These switches have two 10GE SerDes interfaces, one typically ++ used as CPU port. ++ - MxL86282 has eight 2.5 Gigabit PHYs ++ - MxL86252 has five 2.5 Gigabit PHYs +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o ++mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -0,0 +1,675 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++ ++#ifndef __MXL862XX_API_H ++#define __MXL862XX_API_H ++ ++#include ++ ++/** ++ * struct mdio_relay_data - relayed access to the switch internal MDIO bus ++ * @data: data to be read or written ++ * @phy: PHY index ++ * @mmd: MMD device ++ * @reg: register index ++ */ ++struct mdio_relay_data { ++ __le16 data; ++ u8 phy; ++ u8 mmd; ++ __le16 reg; ++} __packed; ++ ++/** ++ * struct mxl862xx_register_mod - Register access parameter to directly ++ * modify internal registers ++ * @addr: Register address offset for modification ++ * @data: Value to write to the register address ++ * @mask: Mask of bits to be modified (1 to modify, 0 to ignore) ++ * ++ * Used for direct register modification operations. ++ */ ++struct mxl862xx_register_mod { ++ __le16 addr; ++ __le16 data; ++ __le16 mask; ++} __packed; ++ ++/** ++ * enum mxl862xx_mac_clear_type - MAC table clear type ++ * @MXL862XX_MAC_CLEAR_PHY_PORT: clear dynamic entries based on port_id ++ * @MXL862XX_MAC_CLEAR_DYNAMIC: clear all dynamic entries ++ */ ++enum mxl862xx_mac_clear_type { ++ MXL862XX_MAC_CLEAR_PHY_PORT = 0, ++ MXL862XX_MAC_CLEAR_DYNAMIC, ++}; ++ ++/** ++ * struct mxl862xx_mac_table_clear - MAC table clear ++ * @type: see &enum mxl862xx_mac_clear_type ++ * @port_id: physical port id ++ */ ++struct mxl862xx_mac_table_clear { ++ u8 type; ++ u8 port_id; ++} __packed; ++ ++/** ++ * enum mxl862xx_age_timer - Aging Timer Value. ++ * @MXL862XX_AGETIMER_1_SEC: 1 second aging time ++ * @MXL862XX_AGETIMER_10_SEC: 10 seconds aging time ++ * @MXL862XX_AGETIMER_300_SEC: 300 seconds aging time ++ * @MXL862XX_AGETIMER_1_HOUR: 1 hour aging time ++ * @MXL862XX_AGETIMER_1_DAY: 24 hours aging time ++ * @MXL862XX_AGETIMER_CUSTOM: Custom aging time in seconds ++ */ ++enum mxl862xx_age_timer { ++ MXL862XX_AGETIMER_1_SEC = 1, ++ MXL862XX_AGETIMER_10_SEC, ++ MXL862XX_AGETIMER_300_SEC, ++ MXL862XX_AGETIMER_1_HOUR, ++ MXL862XX_AGETIMER_1_DAY, ++ MXL862XX_AGETIMER_CUSTOM, ++}; ++ ++/** ++ * struct mxl862xx_bridge_alloc - Bridge Allocation ++ * @bridge_id: If the bridge allocation is successful, a valid ID will be ++ * returned in this field. Otherwise, INVALID_HANDLE is ++ * returned. For bridge free, this field should contain a ++ * valid ID returned by the bridge allocation. ID 0 is not ++ * used for historic reasons. ++ * ++ * Used by MXL862XX_BRIDGE_ALLOC and MXL862XX_BRIDGE_FREE. ++ */ ++struct mxl862xx_bridge_alloc { ++ __le16 bridge_id; ++}; ++ ++/** ++ * enum mxl862xx_bridge_config_mask - Bridge configuration mask ++ * @MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNING_LIMIT: ++ * Mask for mac_learning_limit_enable and mac_learning_limit. ++ * @MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNED_COUNT: ++ * Mask for mac_learning_count ++ * @MXL862XX_BRIDGE_CONFIG_MASK_MAC_DISCARD_COUNT: ++ * Mask for learning_discard_event ++ * @MXL862XX_BRIDGE_CONFIG_MASK_SUB_METER: ++ * Mask for sub_metering_enable and traffic_sub_meter_id ++ * @MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE: ++ * Mask for forward_broadcast, forward_unknown_multicast_ip, ++ * forward_unknown_multicast_non_ip and forward_unknown_unicast. ++ * @MXL862XX_BRIDGE_CONFIG_MASK_ALL: Enable all ++ * @MXL862XX_BRIDGE_CONFIG_MASK_FORCE: Bypass any check for debug purpose ++ */ ++enum mxl862xx_bridge_config_mask { ++ MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNING_LIMIT = BIT(0), ++ MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNED_COUNT = BIT(1), ++ MXL862XX_BRIDGE_CONFIG_MASK_MAC_DISCARD_COUNT = BIT(2), ++ MXL862XX_BRIDGE_CONFIG_MASK_SUB_METER = BIT(3), ++ MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE = BIT(4), ++ MXL862XX_BRIDGE_CONFIG_MASK_ALL = 0x7FFFFFFF, ++ MXL862XX_BRIDGE_CONFIG_MASK_FORCE = BIT(31) ++}; ++ ++/** ++ * enum mxl862xx_bridge_port_egress_meter - Meters for egress traffic type ++ * @MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST: ++ * Index of broadcast traffic meter ++ * @MXL862XX_BRIDGE_PORT_EGRESS_METER_MULTICAST: ++ * Index of known multicast traffic meter ++ * @MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP: ++ * Index of unknown multicast IP traffic meter ++ * @MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP: ++ * Index of unknown multicast non-IP traffic meter ++ * @MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC: ++ * Index of unknown unicast traffic meter ++ * @MXL862XX_BRIDGE_PORT_EGRESS_METER_OTHERS: ++ * Index of traffic meter for other types ++ * @MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX: Number of index ++ */ ++enum mxl862xx_bridge_port_egress_meter { ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST = 0, ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_MULTICAST, ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP, ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP, ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC, ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_OTHERS, ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX, ++}; ++ ++/** ++ * enum mxl862xx_bridge_forward_mode - Bridge forwarding type of packet ++ * @MXL862XX_BRIDGE_FORWARD_FLOOD: Packet is flooded to port members of ++ * ingress bridge port ++ * @MXL862XX_BRIDGE_FORWARD_DISCARD: Packet is discarded ++ */ ++enum mxl862xx_bridge_forward_mode { ++ MXL862XX_BRIDGE_FORWARD_FLOOD = 0, ++ MXL862XX_BRIDGE_FORWARD_DISCARD, ++}; ++ ++/** ++ * struct mxl862xx_bridge_config - Bridge Configuration ++ * @bridge_id: Bridge ID (FID) ++ * @mask: See &enum mxl862xx_bridge_config_mask ++ * @mac_learning_limit_enable: Enable MAC learning limitation ++ * @mac_learning_limit: Max number of MAC addresses that can be learned in ++ * this bridge (all bridge ports) ++ * @mac_learning_count: Number of MAC addresses learned from this bridge ++ * @learning_discard_event: Number of learning discard events due to ++ * hardware resource not available ++ * @sub_metering_enable: Traffic metering on type of traffic (such as ++ * broadcast, multicast, unknown unicast, etc) applies ++ * @traffic_sub_meter_id: Meter for bridge process with specific type (such ++ * as broadcast, multicast, unknown unicast, etc) ++ * @forward_broadcast: Forwarding mode of broadcast traffic. See ++ * &enum mxl862xx_bridge_forward_mode ++ * @forward_unknown_multicast_ip: Forwarding mode of unknown multicast IP ++ * traffic. ++ * See &enum mxl862xx_bridge_forward_mode ++ * @forward_unknown_multicast_non_ip: Forwarding mode of unknown multicast ++ * non-IP traffic. ++ * See &enum mxl862xx_bridge_forward_mode ++ * @forward_unknown_unicast: Forwarding mode of unknown unicast traffic. See ++ * &enum mxl862xx_bridge_forward_mode ++ */ ++struct mxl862xx_bridge_config { ++ __le16 bridge_id; ++ __le32 mask; /* enum mxl862xx_bridge_config_mask */ ++ u8 mac_learning_limit_enable; ++ __le16 mac_learning_limit; ++ __le16 mac_learning_count; ++ __le32 learning_discard_event; ++ u8 sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; ++ __le16 traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; ++ __le32 forward_broadcast; /* enum mxl862xx_bridge_forward_mode */ ++ __le32 forward_unknown_multicast_ip; /* enum mxl862xx_bridge_forward_mode */ ++ __le32 forward_unknown_multicast_non_ip; /* enum mxl862xx_bridge_forward_mode */ ++ __le32 forward_unknown_unicast; /* enum mxl862xx_bridge_forward_mode */ ++} __packed; ++ ++/** ++ * struct mxl862xx_bridge_port_alloc - Bridge Port Allocation ++ * @bridge_port_id: If the bridge port allocation is successful, a valid ID ++ * will be returned in this field. Otherwise, INVALID_HANDLE ++ * is returned. For bridge port free, this field should ++ * contain a valid ID returned by the bridge port allocation. ++ * ++ * Used by MXL862XX_BRIDGE_PORT_ALLOC and MXL862XX_BRIDGE_PORT_FREE. ++ */ ++struct mxl862xx_bridge_port_alloc { ++ __le16 bridge_port_id; ++}; ++ ++/** ++ * enum mxl862xx_bridge_port_config_mask - Bridge Port configuration mask ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID: ++ * Mask for bridge_id ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN: ++ * Mask for ingress_extended_vlan_enable, ++ * ingress_extended_vlan_block_id and ingress_extended_vlan_block_size ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN: ++ * Mask for egress_extended_vlan_enable, egress_extended_vlan_block_id ++ * and egress_extended_vlan_block_size ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_MARKING: ++ * Mask for ingress_marking_mode ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_REMARKING: ++ * Mask for egress_remarking_mode ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_METER: ++ * Mask for ingress_metering_enable and ingress_traffic_meter_id ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER: ++ * Mask for egress_sub_metering_enable and egress_traffic_sub_meter_id ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING: ++ * Mask for dest_logical_port_id, pmapper_enable, dest_sub_if_id_group, ++ * pmapper_mapping_mode, pmapper_id_valid and pmapper ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP: ++ * Mask for bridge_port_map ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_IP_LOOKUP: ++ * Mask for mc_dest_ip_lookup_disable ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_IP_LOOKUP: ++ * Mask for mc_src_ip_lookup_enable ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_MAC_LOOKUP: ++ * Mask for dest_mac_lookup_disable ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING: ++ * Mask for src_mac_learning_disable ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_SPOOFING: ++ * Mask for mac_spoofing_detect_enable ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_PORT_LOCK: ++ * Mask for port_lock_enable ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNING_LIMIT: ++ * Mask for mac_learning_limit_enable and mac_learning_limit ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNED_COUNT: ++ * Mask for mac_learning_count ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER: ++ * Mask for ingress_vlan_filter_enable, ingress_vlan_filter_block_id ++ * and ingress_vlan_filter_block_size ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1: ++ * Mask for bypass_egress_vlan_filter1, egress_vlan_filter1enable, ++ * egress_vlan_filter1block_id and egress_vlan_filter1block_size ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER2: ++ * Mask for egress_vlan_filter2enable, egress_vlan_filter2block_id and ++ * egress_vlan_filter2block_size ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING: ++ * Mask for vlan_tag_selection, vlan_src_mac_priority_enable, ++ * vlan_src_mac_dei_enable, vlan_src_mac_vid_enable, ++ * vlan_dst_mac_priority_enable, vlan_dst_mac_dei_enable and ++ * vlan_dst_mac_vid_enable ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MULTICAST_LOOKUP: ++ * Mask for vlan_multicast_priority_enable, ++ * vlan_multicast_dei_enable and vlan_multicast_vid_enable ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_LOOP_VIOLATION_COUNTER: ++ * Mask for loop_violation_count ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_ALL: Enable all ++ * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE: Bypass any check for debug purpose ++ */ ++enum mxl862xx_bridge_port_config_mask { ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID = BIT(0), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN = BIT(1), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN = BIT(2), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_MARKING = BIT(3), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_REMARKING = BIT(4), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_METER = BIT(5), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER = BIT(6), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING = BIT(7), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP = BIT(8), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_IP_LOOKUP = BIT(9), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_IP_LOOKUP = BIT(10), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_MAC_LOOKUP = BIT(11), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING = BIT(12), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_SPOOFING = BIT(13), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_PORT_LOCK = BIT(14), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNING_LIMIT = BIT(15), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNED_COUNT = BIT(16), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER = BIT(17), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 = BIT(18), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER2 = BIT(19), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING = BIT(20), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MULTICAST_LOOKUP = BIT(21), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_LOOP_VIOLATION_COUNTER = BIT(22), ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_ALL = 0x7FFFFFFF, ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE = BIT(31) ++}; ++ ++/** ++ * enum mxl862xx_color_marking_mode - Color Marking Mode ++ * @MXL862XX_MARKING_ALL_GREEN: mark packets (except critical) to green ++ * @MXL862XX_MARKING_INTERNAL_MARKING: do not change color and priority ++ * @MXL862XX_MARKING_DEI: DEI mark mode ++ * @MXL862XX_MARKING_PCP_8P0D: PCP 8P0D mark mode ++ * @MXL862XX_MARKING_PCP_7P1D: PCP 7P1D mark mode ++ * @MXL862XX_MARKING_PCP_6P2D: PCP 6P2D mark mode ++ * @MXL862XX_MARKING_PCP_5P3D: PCP 5P3D mark mode ++ * @MXL862XX_MARKING_DSCP_AF: DSCP AF class ++ */ ++enum mxl862xx_color_marking_mode { ++ MXL862XX_MARKING_ALL_GREEN = 0, ++ MXL862XX_MARKING_INTERNAL_MARKING, ++ MXL862XX_MARKING_DEI, ++ MXL862XX_MARKING_PCP_8P0D, ++ MXL862XX_MARKING_PCP_7P1D, ++ MXL862XX_MARKING_PCP_6P2D, ++ MXL862XX_MARKING_PCP_5P3D, ++ MXL862XX_MARKING_DSCP_AF, ++}; ++ ++/** ++ * enum mxl862xx_color_remarking_mode - Color Remarking Mode ++ * @MXL862XX_REMARKING_NONE: values from last process stage ++ * @MXL862XX_REMARKING_DEI: DEI mark mode ++ * @MXL862XX_REMARKING_PCP_8P0D: PCP 8P0D mark mode ++ * @MXL862XX_REMARKING_PCP_7P1D: PCP 7P1D mark mode ++ * @MXL862XX_REMARKING_PCP_6P2D: PCP 6P2D mark mode ++ * @MXL862XX_REMARKING_PCP_5P3D: PCP 5P3D mark mode ++ * @MXL862XX_REMARKING_DSCP_AF: DSCP AF class ++ */ ++enum mxl862xx_color_remarking_mode { ++ MXL862XX_REMARKING_NONE = 0, ++ MXL862XX_REMARKING_DEI = 2, ++ MXL862XX_REMARKING_PCP_8P0D, ++ MXL862XX_REMARKING_PCP_7P1D, ++ MXL862XX_REMARKING_PCP_6P2D, ++ MXL862XX_REMARKING_PCP_5P3D, ++ MXL862XX_REMARKING_DSCP_AF, ++}; ++ ++/** ++ * enum mxl862xx_pmapper_mapping_mode - P-mapper Mapping Mode ++ * @MXL862XX_PMAPPER_MAPPING_PCP: Use PCP for VLAN tagged packets to derive ++ * sub interface ID group ++ * @MXL862XX_PMAPPER_MAPPING_LAG: Use LAG Index for Pmapper access ++ * regardless of IP and VLAN packet ++ * @MXL862XX_PMAPPER_MAPPING_DSCP: Use DSCP for VLAN tagged IP packets to ++ * derive sub interface ID group ++ */ ++enum mxl862xx_pmapper_mapping_mode { ++ MXL862XX_PMAPPER_MAPPING_PCP = 0, ++ MXL862XX_PMAPPER_MAPPING_LAG, ++ MXL862XX_PMAPPER_MAPPING_DSCP, ++}; ++ ++/** ++ * struct mxl862xx_pmapper - P-mapper Configuration ++ * @pmapper_id: Index of P-mapper (0-31) ++ * @dest_sub_if_id_group: Sub interface ID group. Entry 0 is for non-IP and ++ * non-VLAN tagged packets. ++ * Entries 1-8 are PCP mapping entries for VLAN tagged ++ * packets. ++ * Entries 9-72 are DSCP or LAG mapping entries. ++ * ++ * Used by CTP port config and bridge port config. In case of LAG, it is ++ * user's responsibility to provide the mapped entries in given P-mapper ++ * table. In other modes the entries are auto mapped from input packet. ++ */ ++struct mxl862xx_pmapper { ++ __le16 pmapper_id; ++ u8 dest_sub_if_id_group[73]; ++} __packed; ++ ++/** ++ * struct mxl862xx_bridge_port_config - Bridge Port Configuration ++ * @bridge_port_id: Bridge Port ID allocated by bridge port allocation ++ * @mask: See &enum mxl862xx_bridge_port_config_mask ++ * @bridge_id: Bridge ID (FID) to which this bridge port is associated ++ * @ingress_extended_vlan_enable: Enable extended VLAN processing for ++ * ingress traffic ++ * @ingress_extended_vlan_block_id: Extended VLAN block allocated for ++ * ingress traffic ++ * @ingress_extended_vlan_block_size: Extended VLAN block size for ingress ++ * traffic ++ * @egress_extended_vlan_enable: Enable extended VLAN processing for egress ++ * traffic ++ * @egress_extended_vlan_block_id: Extended VLAN block allocated for egress ++ * traffic ++ * @egress_extended_vlan_block_size: Extended VLAN block size for egress ++ * traffic ++ * @ingress_marking_mode: Ingress color marking mode. See ++ * &enum mxl862xx_color_marking_mode ++ * @egress_remarking_mode: Color remarking for egress traffic. See ++ * &enum mxl862xx_color_remarking_mode ++ * @ingress_metering_enable: Traffic metering on ingress traffic applies ++ * @ingress_traffic_meter_id: Meter for ingress Bridge Port process ++ * @egress_sub_metering_enable: Traffic metering on various types of egress ++ * traffic ++ * @egress_traffic_sub_meter_id: Meter for egress Bridge Port process with ++ * specific type ++ * @dest_logical_port_id: Destination logical port ++ * @pmapper_enable: Enable P-mapper ++ * @dest_sub_if_id_group: Destination sub interface ID group when ++ * pmapper_enable is false ++ * @pmapper_mapping_mode: P-mapper mapping mode. See ++ * &enum mxl862xx_pmapper_mapping_mode ++ * @pmapper_id_valid: When true, P-mapper is re-used; when false, ++ * allocation is handled by API ++ * @pmapper: P-mapper configuration used when pmapper_enable is true ++ * @bridge_port_map: Port map defining broadcast domain. Each bit ++ * represents one bridge port. Bridge port ID is ++ * index * 16 + bit offset. ++ * @mc_dest_ip_lookup_disable: Disable multicast IP destination table ++ * lookup ++ * @mc_src_ip_lookup_enable: Enable multicast IP source table lookup ++ * @dest_mac_lookup_disable: Disable destination MAC lookup; packet treated ++ * as unknown ++ * @src_mac_learning_disable: Disable source MAC address learning ++ * @mac_spoofing_detect_enable: Enable MAC spoofing detection ++ * @port_lock_enable: Enable port locking ++ * @mac_learning_limit_enable: Enable MAC learning limitation ++ * @mac_learning_limit: Maximum number of MAC addresses that can be learned ++ * from this bridge port ++ * @loop_violation_count: Number of loop violation events from this bridge ++ * port ++ * @mac_learning_count: Number of MAC addresses learned from this bridge ++ * port ++ * @ingress_vlan_filter_enable: Enable ingress VLAN filter ++ * @ingress_vlan_filter_block_id: VLAN filter block of ingress traffic ++ * @ingress_vlan_filter_block_size: VLAN filter block size for ingress ++ * traffic ++ * @bypass_egress_vlan_filter1: For ingress traffic, bypass VLAN filter 1 ++ * at egress bridge port processing ++ * @egress_vlan_filter1enable: Enable egress VLAN filter 1 ++ * @egress_vlan_filter1block_id: VLAN filter block 1 of egress traffic ++ * @egress_vlan_filter1block_size: VLAN filter block 1 size ++ * @egress_vlan_filter2enable: Enable egress VLAN filter 2 ++ * @egress_vlan_filter2block_id: VLAN filter block 2 of egress traffic ++ * @egress_vlan_filter2block_size: VLAN filter block 2 size ++ * @vlan_tag_selection: VLAN tag selection for MAC address/multicast ++ * learning, lookup and filtering. ++ * 0 - Intermediate outer VLAN tag is used. ++ * 1 - Original outer VLAN tag is used. ++ * @vlan_src_mac_priority_enable: Enable VLAN Priority field for source MAC ++ * learning and filtering ++ * @vlan_src_mac_dei_enable: Enable VLAN DEI/CFI field for source MAC ++ * learning and filtering ++ * @vlan_src_mac_vid_enable: Enable VLAN ID field for source MAC learning ++ * and filtering ++ * @vlan_dst_mac_priority_enable: Enable VLAN Priority field for destination ++ * MAC lookup and filtering ++ * @vlan_dst_mac_dei_enable: Enable VLAN CFI/DEI field for destination MAC ++ * lookup and filtering ++ * @vlan_dst_mac_vid_enable: Enable VLAN ID field for destination MAC lookup ++ * and filtering ++ * @vlan_multicast_priority_enable: Enable VLAN Priority field for IP ++ * multicast lookup ++ * @vlan_multicast_dei_enable: Enable VLAN CFI/DEI field for IP multicast ++ * lookup ++ * @vlan_multicast_vid_enable: Enable VLAN ID field for IP multicast lookup ++ */ ++struct mxl862xx_bridge_port_config { ++ __le16 bridge_port_id; ++ __le32 mask; /* enum mxl862xx_bridge_port_config_mask */ ++ __le16 bridge_id; ++ u8 ingress_extended_vlan_enable; ++ __le16 ingress_extended_vlan_block_id; ++ __le16 ingress_extended_vlan_block_size; ++ u8 egress_extended_vlan_enable; ++ __le16 egress_extended_vlan_block_id; ++ __le16 egress_extended_vlan_block_size; ++ __le32 ingress_marking_mode; /* enum mxl862xx_color_marking_mode */ ++ __le32 egress_remarking_mode; /* enum mxl862xx_color_remarking_mode */ ++ u8 ingress_metering_enable; ++ __le16 ingress_traffic_meter_id; ++ u8 egress_sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; ++ __le16 egress_traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; ++ u8 dest_logical_port_id; ++ u8 pmapper_enable; ++ __le16 dest_sub_if_id_group; ++ __le32 pmapper_mapping_mode; /* enum mxl862xx_pmapper_mapping_mode */ ++ u8 pmapper_id_valid; ++ struct mxl862xx_pmapper pmapper; ++ __le16 bridge_port_map[8]; ++ u8 mc_dest_ip_lookup_disable; ++ u8 mc_src_ip_lookup_enable; ++ u8 dest_mac_lookup_disable; ++ u8 src_mac_learning_disable; ++ u8 mac_spoofing_detect_enable; ++ u8 port_lock_enable; ++ u8 mac_learning_limit_enable; ++ __le16 mac_learning_limit; ++ __le16 loop_violation_count; ++ __le16 mac_learning_count; ++ u8 ingress_vlan_filter_enable; ++ __le16 ingress_vlan_filter_block_id; ++ __le16 ingress_vlan_filter_block_size; ++ u8 bypass_egress_vlan_filter1; ++ u8 egress_vlan_filter1enable; ++ __le16 egress_vlan_filter1block_id; ++ __le16 egress_vlan_filter1block_size; ++ u8 egress_vlan_filter2enable; ++ __le16 egress_vlan_filter2block_id; ++ __le16 egress_vlan_filter2block_size; ++ u8 vlan_tag_selection; ++ u8 vlan_src_mac_priority_enable; ++ u8 vlan_src_mac_dei_enable; ++ u8 vlan_src_mac_vid_enable; ++ u8 vlan_dst_mac_priority_enable; ++ u8 vlan_dst_mac_dei_enable; ++ u8 vlan_dst_mac_vid_enable; ++ u8 vlan_multicast_priority_enable; ++ u8 vlan_multicast_dei_enable; ++ u8 vlan_multicast_vid_enable; ++} __packed; ++ ++/** ++ * struct mxl862xx_cfg - Global Switch configuration Attributes ++ * @mac_table_age_timer: See &enum mxl862xx_age_timer ++ * @age_timer: Custom MAC table aging timer in seconds ++ * @max_packet_len: Maximum Ethernet packet length ++ * @learning_limit_action: Automatic MAC address table learning limitation ++ * consecutive action ++ * @mac_locking_action: Accept or discard MAC port locking violation ++ * packets ++ * @mac_spoofing_action: Accept or discard MAC spoofing and port MAC locking ++ * violation packets ++ * @pause_mac_mode_src: Pause frame MAC source address mode ++ * @pause_mac_src: Pause frame MAC source address ++ */ ++struct mxl862xx_cfg { ++ __le32 mac_table_age_timer; /* enum mxl862xx_age_timer */ ++ __le32 age_timer; ++ __le16 max_packet_len; ++ u8 learning_limit_action; ++ u8 mac_locking_action; ++ u8 mac_spoofing_action; ++ u8 pause_mac_mode_src; ++ u8 pause_mac_src[ETH_ALEN]; ++} __packed; ++ ++/** ++ * enum mxl862xx_ss_sp_tag_mask - Special tag valid field indicator bits ++ * @MXL862XX_SS_SP_TAG_MASK_RX: valid RX special tag mode ++ * @MXL862XX_SS_SP_TAG_MASK_TX: valid TX special tag mode ++ * @MXL862XX_SS_SP_TAG_MASK_RX_PEN: valid RX special tag info over preamble ++ * @MXL862XX_SS_SP_TAG_MASK_TX_PEN: valid TX special tag info over preamble ++ */ ++enum mxl862xx_ss_sp_tag_mask { ++ MXL862XX_SS_SP_TAG_MASK_RX = BIT(0), ++ MXL862XX_SS_SP_TAG_MASK_TX = BIT(1), ++ MXL862XX_SS_SP_TAG_MASK_RX_PEN = BIT(2), ++ MXL862XX_SS_SP_TAG_MASK_TX_PEN = BIT(3), ++}; ++ ++/** ++ * enum mxl862xx_ss_sp_tag_rx - RX special tag mode ++ * @MXL862XX_SS_SP_TAG_RX_NO_TAG_NO_INSERT: packet does NOT have special ++ * tag and special tag is NOT inserted ++ * @MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT: packet does NOT have special tag ++ * and special tag is inserted ++ * @MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT: packet has special tag and special ++ * tag is NOT inserted ++ */ ++enum mxl862xx_ss_sp_tag_rx { ++ MXL862XX_SS_SP_TAG_RX_NO_TAG_NO_INSERT = 0, ++ MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT = 1, ++ MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT = 2, ++}; ++ ++/** ++ * enum mxl862xx_ss_sp_tag_tx - TX special tag mode ++ * @MXL862XX_SS_SP_TAG_TX_NO_TAG_NO_REMOVE: packet does NOT have special ++ * tag and special tag is NOT removed ++ * @MXL862XX_SS_SP_TAG_TX_TAG_REPLACE: packet has special tag and special ++ * tag is replaced ++ * @MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE: packet has special tag and special ++ * tag is NOT removed ++ * @MXL862XX_SS_SP_TAG_TX_TAG_REMOVE: packet has special tag and special ++ * tag is removed ++ */ ++enum mxl862xx_ss_sp_tag_tx { ++ MXL862XX_SS_SP_TAG_TX_NO_TAG_NO_REMOVE = 0, ++ MXL862XX_SS_SP_TAG_TX_TAG_REPLACE = 1, ++ MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE = 2, ++ MXL862XX_SS_SP_TAG_TX_TAG_REMOVE = 3, ++}; ++ ++/** ++ * enum mxl862xx_ss_sp_tag_rx_pen - RX special tag info over preamble ++ * @MXL862XX_SS_SP_TAG_RX_PEN_ALL_0: special tag info inserted from byte 2 ++ * to 7 are all 0 ++ * @MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_IS_16: special tag byte 5 is 16, other ++ * bytes from 2 to 7 are 0 ++ * @MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_FROM_PREAMBLE: special tag byte 5 is ++ * from preamble field, others ++ * are 0 ++ * @MXL862XX_SS_SP_TAG_RX_PEN_BYTE_2_TO_7_FROM_PREAMBLE: special tag byte 2 ++ * to 7 are from preamble ++ * field ++ */ ++enum mxl862xx_ss_sp_tag_rx_pen { ++ MXL862XX_SS_SP_TAG_RX_PEN_ALL_0 = 0, ++ MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_IS_16 = 1, ++ MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_FROM_PREAMBLE = 2, ++ MXL862XX_SS_SP_TAG_RX_PEN_BYTE_2_TO_7_FROM_PREAMBLE = 3, ++}; ++ ++/** ++ * struct mxl862xx_ss_sp_tag - Special tag port settings ++ * @pid: port ID (1~16) ++ * @mask: See &enum mxl862xx_ss_sp_tag_mask ++ * @rx: See &enum mxl862xx_ss_sp_tag_rx ++ * @tx: See &enum mxl862xx_ss_sp_tag_tx ++ * @rx_pen: See &enum mxl862xx_ss_sp_tag_rx_pen ++ * @tx_pen: TX special tag info over preamble ++ * 0 - disabled ++ * 1 - enabled ++ */ ++struct mxl862xx_ss_sp_tag { ++ u8 pid; ++ u8 mask; /* enum mxl862xx_ss_sp_tag_mask */ ++ u8 rx; /* enum mxl862xx_ss_sp_tag_rx */ ++ u8 tx; /* enum mxl862xx_ss_sp_tag_tx */ ++ u8 rx_pen; /* enum mxl862xx_ss_sp_tag_rx_pen */ ++ u8 tx_pen; /* boolean */ ++} __packed; ++ ++/** ++ * enum mxl862xx_logical_port_mode - Logical port mode ++ * @MXL862XX_LOGICAL_PORT_8BIT_WLAN: WLAN with 8-bit station ID ++ * @MXL862XX_LOGICAL_PORT_9BIT_WLAN: WLAN with 9-bit station ID ++ * @MXL862XX_LOGICAL_PORT_ETHERNET: Ethernet port ++ * @MXL862XX_LOGICAL_PORT_OTHER: Others ++ */ ++enum mxl862xx_logical_port_mode { ++ MXL862XX_LOGICAL_PORT_8BIT_WLAN = 0, ++ MXL862XX_LOGICAL_PORT_9BIT_WLAN, ++ MXL862XX_LOGICAL_PORT_ETHERNET, ++ MXL862XX_LOGICAL_PORT_OTHER = 0xFF, ++}; ++ ++/** ++ * struct mxl862xx_ctp_port_assignment - CTP Port Assignment/association ++ * with logical port ++ * @logical_port_id: Logical Port Id. The valid range is hardware dependent ++ * @first_ctp_port_id: First CTP (Connectivity Termination Port) ID mapped ++ * to above logical port ID ++ * @number_of_ctp_port: Total number of CTP Ports mapped above logical port ++ * ID ++ * @mode: Logical port mode to define sub interface ID format. See ++ * &enum mxl862xx_logical_port_mode ++ * @bridge_port_id: Bridge Port ID (not FID). For allocation, each CTP ++ * allocated is mapped to the Bridge Port given by this field. ++ * The Bridge Port will be configured to use first CTP as ++ * egress CTP. ++ */ ++struct mxl862xx_ctp_port_assignment { ++ u8 logical_port_id; ++ __le16 first_ctp_port_id; ++ __le16 number_of_ctp_port; ++ __le32 mode; /* enum mxl862xx_logical_port_mode */ ++ __le16 bridge_port_id; ++} __packed; ++ ++/** ++ * struct mxl862xx_sys_fw_image_version - Firmware version information ++ * @iv_major: firmware major version ++ * @iv_minor: firmware minor version ++ * @iv_revision: firmware revision ++ * @iv_build_num: firmware build number ++ */ ++struct mxl862xx_sys_fw_image_version { ++ u8 iv_major; ++ u8 iv_minor; ++ __le16 iv_revision; ++ __le32 iv_build_num; ++} __packed; ++ ++#endif /* __MXL862XX_API_H */ +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -0,0 +1,49 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++ ++#ifndef __MXL862XX_CMD_H ++#define __MXL862XX_CMD_H ++ ++#define MXL862XX_MMD_DEV 30 ++#define MXL862XX_MMD_REG_CTRL 0 ++#define MXL862XX_MMD_REG_LEN_RET 1 ++#define MXL862XX_MMD_REG_DATA_FIRST 2 ++#define MXL862XX_MMD_REG_DATA_LAST 95 ++#define MXL862XX_MMD_REG_DATA_MAX_SIZE \ ++ (MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1) ++ ++#define MXL862XX_COMMON_MAGIC 0x100 ++#define MXL862XX_BRDG_MAGIC 0x300 ++#define MXL862XX_BRDGPORT_MAGIC 0x400 ++#define MXL862XX_CTP_MAGIC 0x500 ++#define MXL862XX_SWMAC_MAGIC 0xa00 ++#define MXL862XX_SS_MAGIC 0x1600 ++#define GPY_GPY2XX_MAGIC 0x1800 ++#define SYS_MISC_MAGIC 0x1900 ++ ++#define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9) ++#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11) ++ ++#define MXL862XX_BRIDGE_ALLOC (MXL862XX_BRDG_MAGIC + 0x1) ++#define MXL862XX_BRIDGE_CONFIGSET (MXL862XX_BRDG_MAGIC + 0x2) ++#define MXL862XX_BRIDGE_CONFIGGET (MXL862XX_BRDG_MAGIC + 0x3) ++#define MXL862XX_BRIDGE_FREE (MXL862XX_BRDG_MAGIC + 0x4) ++ ++#define MXL862XX_BRIDGEPORT_ALLOC (MXL862XX_BRDGPORT_MAGIC + 0x1) ++#define MXL862XX_BRIDGEPORT_CONFIGSET (MXL862XX_BRDGPORT_MAGIC + 0x2) ++#define MXL862XX_BRIDGEPORT_CONFIGGET (MXL862XX_BRDGPORT_MAGIC + 0x3) ++#define MXL862XX_BRIDGEPORT_FREE (MXL862XX_BRDGPORT_MAGIC + 0x4) ++ ++#define MXL862XX_CTP_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3) ++ ++#define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8) ++ ++#define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x02) ++ ++#define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x01) ++#define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x02) ++ ++#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x02) ++ ++#define MMD_API_MAXIMUM_ID 0x7fff ++ ++#endif /* __MXL862XX_CMD_H */ +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c +@@ -0,0 +1,245 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Based upon the MaxLinear SDK driver ++ * ++ * Copyright (C) 2025 Daniel Golle ++ * Copyright (C) 2025 John Crispin ++ * Copyright (C) 2024 MaxLinear Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "mxl862xx.h" ++#include "mxl862xx-host.h" ++ ++#define CTRL_BUSY_MASK BIT(15) ++ ++#define MXL862XX_MMD_REG_CTRL 0 ++#define MXL862XX_MMD_REG_LEN_RET 1 ++#define MXL862XX_MMD_REG_DATA_FIRST 2 ++#define MXL862XX_MMD_REG_DATA_LAST 95 ++#define MXL862XX_MMD_REG_DATA_MAX_SIZE \ ++ (MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1) ++ ++#define MMD_API_SET_DATA_0 2 ++#define MMD_API_GET_DATA_0 5 ++#define MMD_API_RST_DATA 8 ++ ++#define MXL862XX_SWITCH_RESET 0x9907 ++ ++static int mxl862xx_reg_read(struct mxl862xx_priv *priv, u32 addr) ++{ ++ return __mdiodev_c45_read(priv->mdiodev, MDIO_MMD_VEND1, addr); ++} ++ ++static int mxl862xx_reg_write(struct mxl862xx_priv *priv, u32 addr, u16 data) ++{ ++ return __mdiodev_c45_write(priv->mdiodev, MDIO_MMD_VEND1, addr, data); ++} ++ ++static int mxl862xx_ctrl_read(struct mxl862xx_priv *priv) ++{ ++ return mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL); ++} ++ ++static int mxl862xx_busy_wait(struct mxl862xx_priv *priv) ++{ ++ int val; ++ ++ return readx_poll_timeout(mxl862xx_ctrl_read, priv, val, ++ !(val & CTRL_BUSY_MASK), 15, 500000); ++} ++ ++static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words) ++{ ++ int ret; ++ u16 cmd; ++ ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, ++ MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); ++ if (ret < 0) ++ return ret; ++ ++ cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1; ++ if (!(cmd < 2)) ++ return -EINVAL; ++ ++ cmd += MMD_API_SET_DATA_0; ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, ++ cmd | CTRL_BUSY_MASK); ++ if (ret < 0) ++ return ret; ++ ++ return mxl862xx_busy_wait(priv); ++} ++ ++static int mxl862xx_get_data(struct mxl862xx_priv *priv, u16 words) ++{ ++ int ret; ++ u16 cmd; ++ ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, ++ MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); ++ if (ret < 0) ++ return ret; ++ ++ cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE; ++ if (!(cmd > 0 && cmd < 3)) ++ return -EINVAL; ++ ++ cmd += MMD_API_GET_DATA_0; ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, ++ cmd | CTRL_BUSY_MASK); ++ if (ret < 0) ++ return ret; ++ ++ return mxl862xx_busy_wait(priv); ++} ++ ++static int mxl862xx_firmware_return(int ret) ++{ ++ /* Only 16-bit values are valid. */ ++ if (WARN_ON(ret & GENMASK(31, 16))) ++ return -EINVAL; ++ ++ /* Interpret value as signed 16-bit integer. */ ++ return (s16)ret; ++} ++ ++static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size, ++ bool quiet) ++{ ++ int ret; ++ ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, size); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, ++ cmd | CTRL_BUSY_MASK); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_busy_wait(priv); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET); ++ if (ret < 0) ++ return ret; ++ ++ /* handle errors returned by the firmware as -EIO ++ * The firmware is based on Zephyr OS and uses the errors as ++ * defined in errno.h of Zephyr OS. See ++ * https://github.com/zephyrproject-rtos/zephyr/blob/v3.7.0/lib/libc/minimal/include/errno.h ++ */ ++ ret = mxl862xx_firmware_return(ret); ++ if (ret < 0) { ++ if (!quiet) ++ dev_err(&priv->mdiodev->dev, ++ "CMD %04x returned error %d\n", cmd, ret); ++ return -EIO; ++ } ++ ++ return ret; ++} ++ ++int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data, ++ u16 size, bool read, bool quiet) ++{ ++ __le16 *data = _data; ++ int ret, cmd_ret; ++ u16 max, i; ++ ++ dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data); ++ ++ mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ max = (size + 1) / 2; ++ ++ ret = mxl862xx_busy_wait(priv); ++ if (ret < 0) ++ goto out; ++ ++ for (i = 0; i < max; i++) { ++ u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; ++ ++ if (i && off == 0) { ++ /* Send command to set data when every ++ * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written. ++ */ ++ ret = mxl862xx_set_data(priv, i); ++ if (ret < 0) ++ goto out; ++ } ++ ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_DATA_FIRST + off, ++ le16_to_cpu(data[i])); ++ if (ret < 0) ++ goto out; ++ } ++ ++ ret = mxl862xx_send_cmd(priv, cmd, size, quiet); ++ if (ret < 0 || !read) ++ goto out; ++ ++ /* store result of mxl862xx_send_cmd() */ ++ cmd_ret = ret; ++ ++ for (i = 0; i < max; i++) { ++ u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; ++ ++ if (i && off == 0) { ++ /* Send command to fetch next batch of data when every ++ * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are read. ++ */ ++ ret = mxl862xx_get_data(priv, i); ++ if (ret < 0) ++ goto out; ++ } ++ ++ ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_DATA_FIRST + off); ++ if (ret < 0) ++ goto out; ++ ++ if ((i * 2 + 1) == size) { ++ /* Special handling for last BYTE if it's not WORD ++ * aligned to avoid writing beyond the allocated data ++ * structure. ++ */ ++ *(uint8_t *)&data[i] = ret & 0xff; ++ } else { ++ data[i] = cpu_to_le16((u16)ret); ++ } ++ } ++ ++ /* on success return the result of the mxl862xx_send_cmd() */ ++ ret = cmd_ret; ++ ++ dev_dbg(&priv->mdiodev->dev, "RET %d DATA %*ph\n", ret, size, data); ++ ++out: ++ mutex_unlock(&priv->mdiodev->bus->mdio_lock); ++ ++ return ret; ++} ++ ++int mxl862xx_reset(struct mxl862xx_priv *priv) ++{ ++ int ret; ++ ++ mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ /* Software reset */ ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 0); ++ if (ret) ++ goto out; ++ ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, MXL862XX_SWITCH_RESET); ++out: ++ mutex_unlock(&priv->mdiodev->bus->mdio_lock); ++ ++ return ret; ++} +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.h +@@ -0,0 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++ ++#ifndef __MXL862XX_HOST_H ++#define __MXL862XX_HOST_H ++ ++#include "mxl862xx.h" ++ ++int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *data, u16 size, ++ bool read, bool quiet); ++int mxl862xx_reset(struct mxl862xx_priv *priv); ++ ++#endif /* __MXL862XX_HOST_H */ +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -0,0 +1,476 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Driver for MaxLinear MxL862xx switch family ++ * ++ * Copyright (C) 2024 MaxLinear Inc. ++ * Copyright (C) 2025 John Crispin ++ * Copyright (C) 2025 Daniel Golle ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mxl862xx.h" ++#include "mxl862xx-api.h" ++#include "mxl862xx-cmd.h" ++#include "mxl862xx-host.h" ++ ++#define MXL862XX_API_WRITE(dev, cmd, data) \ ++ mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) ++#define MXL862XX_API_READ(dev, cmd, data) \ ++ mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, false) ++#define MXL862XX_API_READ_QUIET(dev, cmd, data) \ ++ mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) ++ ++#define MXL862XX_SDMA_PCTRLP(p) (0xbc0 + ((p) * 0x6)) ++#define MXL862XX_SDMA_PCTRL_EN BIT(0) ++ ++#define MXL862XX_FDMA_PCTRLP(p) (0xa80 + ((p) * 0x6)) ++#define MXL862XX_FDMA_PCTRL_EN BIT(0) ++ ++#define MXL862XX_READY_TIMEOUT_MS 10000 ++#define MXL862XX_READY_POLL_MS 100 ++ ++static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds, ++ int port, ++ enum dsa_tag_protocol m) ++{ ++ return DSA_TAG_PROTO_MXL862; ++} ++ ++/* PHY access via firmware relay */ ++static int mxl862xx_phy_read_mmd(struct mxl862xx_priv *priv, int port, ++ int devadd, int reg) ++{ ++ struct mdio_relay_data param = { ++ .phy = port, ++ .mmd = devadd, ++ .reg = cpu_to_le16(reg), ++ }; ++ int ret; ++ ++ ret = MXL862XX_API_READ(priv, INT_GPHY_READ, param); ++ if (ret) ++ return ret; ++ ++ return le16_to_cpu(param.data); ++} ++ ++static int mxl862xx_phy_write_mmd(struct mxl862xx_priv *priv, int port, ++ int devadd, int reg, u16 data) ++{ ++ struct mdio_relay_data param = { ++ .phy = port, ++ .mmd = devadd, ++ .reg = cpu_to_le16(reg), ++ .data = cpu_to_le16(data), ++ }; ++ ++ return MXL862XX_API_WRITE(priv, INT_GPHY_WRITE, param); ++} ++ ++static int mxl862xx_phy_read_mii_bus(struct mii_bus *bus, int port, int regnum) ++{ ++ return mxl862xx_phy_read_mmd(bus->priv, port, 0, regnum); ++} ++ ++static int mxl862xx_phy_write_mii_bus(struct mii_bus *bus, int port, ++ int regnum, u16 val) ++{ ++ return mxl862xx_phy_write_mmd(bus->priv, port, 0, regnum, val); ++} ++ ++static int mxl862xx_phy_read_c45_mii_bus(struct mii_bus *bus, int port, ++ int devadd, int regnum) ++{ ++ return mxl862xx_phy_read_mmd(bus->priv, port, devadd, regnum); ++} ++ ++static int mxl862xx_phy_write_c45_mii_bus(struct mii_bus *bus, int port, ++ int devadd, int regnum, u16 val) ++{ ++ return mxl862xx_phy_write_mmd(bus->priv, port, devadd, regnum, val); ++} ++ ++static int mxl862xx_wait_ready(struct dsa_switch *ds) ++{ ++ struct mxl862xx_sys_fw_image_version ver = {}; ++ unsigned long start = jiffies, timeout; ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_cfg cfg = {}; ++ int ret; ++ ++ timeout = start + msecs_to_jiffies(MXL862XX_READY_TIMEOUT_MS); ++ msleep(2000); /* it always takes at least 2 seconds */ ++ do { ++ ret = MXL862XX_API_READ_QUIET(priv, SYS_MISC_FW_VERSION, ver); ++ if (ret || !ver.iv_major) ++ goto not_ready_yet; ++ ++ /* being able to perform CFGGET indicates that ++ * the firmware is ready ++ */ ++ ret = MXL862XX_API_READ_QUIET(priv, ++ MXL862XX_COMMON_CFGGET, ++ cfg); ++ if (ret) ++ goto not_ready_yet; ++ ++ dev_info(ds->dev, "switch ready after %ums, firmware %u.%u.%u (build %u)\n", ++ jiffies_to_msecs(jiffies - start), ++ ver.iv_major, ver.iv_minor, ++ le16_to_cpu(ver.iv_revision), ++ le32_to_cpu(ver.iv_build_num)); ++ return 0; ++ ++not_ready_yet: ++ msleep(MXL862XX_READY_POLL_MS); ++ } while (time_before(jiffies, timeout)); ++ ++ dev_err(ds->dev, "switch not responding after reset\n"); ++ return -ETIMEDOUT; ++} ++ ++static int mxl862xx_setup_mdio(struct dsa_switch *ds) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct device *dev = ds->dev; ++ struct device_node *mdio_np; ++ struct mii_bus *bus; ++ int ret; ++ ++ bus = devm_mdiobus_alloc(dev); ++ if (!bus) ++ return -ENOMEM; ++ ++ bus->priv = priv; ++ ds->user_mii_bus = bus; ++ bus->name = KBUILD_MODNAME "-mii"; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev)); ++ bus->read_c45 = mxl862xx_phy_read_c45_mii_bus; ++ bus->write_c45 = mxl862xx_phy_write_c45_mii_bus; ++ bus->read = mxl862xx_phy_read_mii_bus; ++ bus->write = mxl862xx_phy_write_mii_bus; ++ bus->parent = dev; ++ bus->phy_mask = ~ds->phys_mii_mask; ++ ++ mdio_np = of_get_child_by_name(dev->of_node, "mdio"); ++ if (!mdio_np) ++ return -ENODEV; ++ ++ ret = devm_of_mdiobus_register(dev, bus, mdio_np); ++ of_node_put(mdio_np); ++ ++ return ret; ++} ++ ++static int mxl862xx_setup(struct dsa_switch *ds) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ int ret; ++ ++ ret = mxl862xx_reset(priv); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_wait_ready(ds); ++ if (ret) ++ return ret; ++ ++ return mxl862xx_setup_mdio(ds); ++} ++ ++static int mxl862xx_port_state(struct dsa_switch *ds, int port, bool enable) ++{ ++ struct mxl862xx_register_mod sdma = { ++ .addr = cpu_to_le16(MXL862XX_SDMA_PCTRLP(port)), ++ .data = cpu_to_le16(enable ? MXL862XX_SDMA_PCTRL_EN : 0), ++ .mask = cpu_to_le16(MXL862XX_SDMA_PCTRL_EN), ++ }; ++ struct mxl862xx_register_mod fdma = { ++ .addr = cpu_to_le16(MXL862XX_FDMA_PCTRLP(port)), ++ .data = cpu_to_le16(enable ? MXL862XX_FDMA_PCTRL_EN : 0), ++ .mask = cpu_to_le16(MXL862XX_FDMA_PCTRL_EN), ++ }; ++ int ret; ++ ++ ret = MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_REGISTERMOD, sdma); ++ if (ret) ++ return ret; ++ ++ return MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_REGISTERMOD, fdma); ++} ++ ++static int mxl862xx_port_enable(struct dsa_switch *ds, int port, ++ struct phy_device *phydev) ++{ ++ return mxl862xx_port_state(ds, port, true); ++} ++ ++static void mxl862xx_port_disable(struct dsa_switch *ds, int port) ++{ ++ if (mxl862xx_port_state(ds, port, false)) ++ dev_err(ds->dev, "failed to disable port %d\n", port); ++} ++ ++static void mxl862xx_port_fast_age(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_mac_table_clear param = { ++ .type = MXL862XX_MAC_CLEAR_PHY_PORT, ++ .port_id = port, ++ }; ++ ++ if (MXL862XX_API_WRITE(ds->priv, MXL862XX_MAC_TABLECLEARCOND, param)) ++ dev_err(ds->dev, "failed to clear fdb on port %d\n", port); ++} ++ ++static int mxl862xx_configure_ctp_port(struct dsa_switch *ds, int port, ++ u16 first_ctp_port_id, ++ u16 number_of_ctp_ports) ++{ ++ struct mxl862xx_ctp_port_assignment ctp_assign = { ++ .logical_port_id = port, ++ .first_ctp_port_id = cpu_to_le16(first_ctp_port_id), ++ .number_of_ctp_port = cpu_to_le16(number_of_ctp_ports), ++ .mode = cpu_to_le32(MXL862XX_LOGICAL_PORT_ETHERNET), ++ }; ++ ++ return MXL862XX_API_WRITE(ds->priv, MXL862XX_CTP_PORTASSIGNMENTSET, ++ ctp_assign); ++} ++ ++static int mxl862xx_configure_sp_tag_proto(struct dsa_switch *ds, int port, ++ bool enable) ++{ ++ struct mxl862xx_ss_sp_tag tag = { ++ .pid = port, ++ .mask = MXL862XX_SS_SP_TAG_MASK_RX | MXL862XX_SS_SP_TAG_MASK_TX, ++ .rx = enable ? MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT : ++ MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT, ++ .tx = enable ? MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE : ++ MXL862XX_SS_SP_TAG_TX_TAG_REMOVE, ++ }; ++ ++ return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag); ++} ++ ++static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_bridge_port_config br_port_cfg = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ u16 bridge_port_map = 0; ++ struct dsa_port *dp; ++ ++ /* CPU port bridge setup */ ++ br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); ++ ++ br_port_cfg.bridge_port_id = cpu_to_le16(port); ++ br_port_cfg.src_mac_learning_disable = false; ++ br_port_cfg.vlan_src_mac_vid_enable = true; ++ br_port_cfg.vlan_dst_mac_vid_enable = true; ++ ++ /* include all assigned user ports in the CPU portmap */ ++ dsa_switch_for_each_user_port(dp, ds) { ++ /* it's safe to rely on cpu_dp being valid for user ports */ ++ if (dp->cpu_dp->index != port) ++ continue; ++ ++ bridge_port_map |= BIT(dp->index); ++ } ++ br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map); ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg); ++} ++ ++static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_bridge_port_config br_port_cfg = {}; ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct mxl862xx_bridge_alloc br_alloc = {}; ++ int ret; ++ ++ ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc); ++ if (ret) { ++ dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port); ++ return ret; ++ } ++ ++ br_port_cfg.bridge_id = br_alloc.bridge_id; ++ br_port_cfg.bridge_port_id = cpu_to_le16(port); ++ br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); ++ br_port_cfg.src_mac_learning_disable = true; ++ br_port_cfg.vlan_src_mac_vid_enable = false; ++ br_port_cfg.vlan_dst_mac_vid_enable = false; ++ /* As this function is only called for user ports it is safe to rely on ++ * cpu_dp being valid ++ */ ++ br_port_cfg.bridge_port_map[0] = cpu_to_le16(BIT(dp->cpu_dp->index)); ++ ++ return MXL862XX_API_WRITE(ds->priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg); ++} ++ ++static int mxl862xx_port_setup(struct dsa_switch *ds, int port) ++{ ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ bool is_cpu_port = dsa_port_is_cpu(dp); ++ int ret; ++ ++ /* disable port and flush MAC entries */ ++ ret = mxl862xx_port_state(ds, port, false); ++ if (ret) ++ return ret; ++ ++ mxl862xx_port_fast_age(ds, port); ++ ++ /* skip setup for unused and DSA ports */ ++ if (dsa_port_is_unused(dp) || ++ dsa_port_is_dsa(dp)) ++ return 0; ++ ++ /* configure tag protocol */ ++ ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port); ++ if (ret) ++ return ret; ++ ++ /* assign CTP port IDs */ ++ ret = mxl862xx_configure_ctp_port(ds, port, port, ++ is_cpu_port ? 32 - port : 1); ++ if (ret) ++ return ret; ++ ++ if (is_cpu_port) ++ /* assign user ports to CPU port bridge */ ++ return mxl862xx_setup_cpu_bridge(ds, port); ++ ++ /* setup single-port bridge for user ports */ ++ return mxl862xx_add_single_port_bridge(ds, port); ++} ++ ++static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) ++{ ++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | ++ MAC_100 | MAC_1000 | MAC_2500FD; ++ ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); ++} ++ ++static const struct dsa_switch_ops mxl862xx_switch_ops = { ++ .get_tag_protocol = mxl862xx_get_tag_protocol, ++ .setup = mxl862xx_setup, ++ .port_setup = mxl862xx_port_setup, ++ .phylink_get_caps = mxl862xx_phylink_get_caps, ++ .port_enable = mxl862xx_port_enable, ++ .port_disable = mxl862xx_port_disable, ++ .port_fast_age = mxl862xx_port_fast_age, ++}; ++ ++static void mxl862xx_phylink_mac_config(struct phylink_config *config, ++ unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++} ++ ++static void mxl862xx_phylink_mac_link_down(struct phylink_config *config, ++ unsigned int mode, ++ phy_interface_t interface) ++{ ++} ++ ++static void mxl862xx_phylink_mac_link_up(struct phylink_config *config, ++ struct phy_device *phydev, ++ unsigned int mode, ++ phy_interface_t interface, ++ int speed, int duplex, ++ bool tx_pause, bool rx_pause) ++{ ++} ++ ++static const struct phylink_mac_ops mxl862xx_phylink_mac_ops = { ++ .mac_config = mxl862xx_phylink_mac_config, ++ .mac_link_down = mxl862xx_phylink_mac_link_down, ++ .mac_link_up = mxl862xx_phylink_mac_link_up, ++}; ++ ++static int mxl862xx_probe(struct mdio_device *mdiodev) ++{ ++ struct device *dev = &mdiodev->dev; ++ struct mxl862xx_priv *priv; ++ struct dsa_switch *ds; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->mdiodev = mdiodev; ++ ++ ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); ++ if (!ds) ++ return -ENOMEM; ++ ++ priv->ds = ds; ++ ds->dev = dev; ++ ds->priv = priv; ++ ds->ops = &mxl862xx_switch_ops; ++ ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops; ++ ds->num_ports = MXL862XX_MAX_PORTS; ++ ++ dev_set_drvdata(dev, ds); ++ ++ return dsa_register_switch(ds); ++} ++ ++static void mxl862xx_remove(struct mdio_device *mdiodev) ++{ ++ struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); ++ ++ if (!ds) ++ return; ++ ++ dsa_unregister_switch(ds); ++} ++ ++static void mxl862xx_shutdown(struct mdio_device *mdiodev) ++{ ++ struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); ++ ++ if (!ds) ++ return; ++ ++ dsa_switch_shutdown(ds); ++ ++ dev_set_drvdata(&mdiodev->dev, NULL); ++} ++ ++static const struct of_device_id mxl862xx_of_match[] = { ++ { .compatible = "maxlinear,mxl86282" }, ++ { .compatible = "maxlinear,mxl86252" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, mxl862xx_of_match); ++ ++static struct mdio_driver mxl862xx_driver = { ++ .probe = mxl862xx_probe, ++ .remove = mxl862xx_remove, ++ .shutdown = mxl862xx_shutdown, ++ .mdiodrv.driver = { ++ .name = "mxl862xx", ++ .of_match_table = mxl862xx_of_match, ++ }, ++}; ++ ++mdio_module_driver(mxl862xx_driver); ++ ++MODULE_DESCRIPTION("Driver for MaxLinear MxL862xx switch family"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++ ++#ifndef __MXL862XX_H ++#define __MXL862XX_H ++ ++#include ++#include ++ ++#define MXL862XX_MAX_PORTS 17 ++ ++struct mxl862xx_priv { ++ struct dsa_switch *ds; ++ struct mdio_device *mdiodev; ++}; ++ ++#endif /* __MXL862XX_H */ diff --git a/target/linux/generic/backport-6.12/765-v7.0-net-dsa-mxl862xx-rename-MDIO-op-arguments.patch b/target/linux/generic/backport-6.12/765-v7.0-net-dsa-mxl862xx-rename-MDIO-op-arguments.patch new file mode 100644 index 00000000000..b4788069c6b --- /dev/null +++ b/target/linux/generic/backport-6.12/765-v7.0-net-dsa-mxl862xx-rename-MDIO-op-arguments.patch @@ -0,0 +1,94 @@ +From b5f8b39d22ab93cada5c88dc2cb6495b95f44c70 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 3 Mar 2026 03:17:43 +0000 +Subject: [PATCH 04/35] net: dsa: mxl862xx: rename MDIO op arguments + +The use of the 'port' argument name for functions implementing the MDIO +bus operations is misleading as the port address isn't equal to the +PHY address. + +Rename the MDIO operation argument name to match the prototypes of +mdiobus_write, mdiobus_read, mdiobus_c45_read and mdiobus_c45_write. + +Suggested-by: Vladimir Oltean +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/e1f4cb3bcffc7df9af0f2c9b673b14c7e1201c9a.1772507674.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mxl862xx/mxl862xx.c | 32 ++++++++++++++--------------- + 1 file changed, 16 insertions(+), 16 deletions(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -44,13 +44,13 @@ static enum dsa_tag_protocol mxl862xx_ge + } + + /* PHY access via firmware relay */ +-static int mxl862xx_phy_read_mmd(struct mxl862xx_priv *priv, int port, +- int devadd, int reg) ++static int mxl862xx_phy_read_mmd(struct mxl862xx_priv *priv, int addr, ++ int devadd, int regnum) + { + struct mdio_relay_data param = { +- .phy = port, ++ .phy = addr, + .mmd = devadd, +- .reg = cpu_to_le16(reg), ++ .reg = cpu_to_le16(regnum), + }; + int ret; + +@@ -61,40 +61,40 @@ static int mxl862xx_phy_read_mmd(struct + return le16_to_cpu(param.data); + } + +-static int mxl862xx_phy_write_mmd(struct mxl862xx_priv *priv, int port, +- int devadd, int reg, u16 data) ++static int mxl862xx_phy_write_mmd(struct mxl862xx_priv *priv, int addr, ++ int devadd, int regnum, u16 data) + { + struct mdio_relay_data param = { +- .phy = port, ++ .phy = addr, + .mmd = devadd, +- .reg = cpu_to_le16(reg), ++ .reg = cpu_to_le16(regnum), + .data = cpu_to_le16(data), + }; + + return MXL862XX_API_WRITE(priv, INT_GPHY_WRITE, param); + } + +-static int mxl862xx_phy_read_mii_bus(struct mii_bus *bus, int port, int regnum) ++static int mxl862xx_phy_read_mii_bus(struct mii_bus *bus, int addr, int regnum) + { +- return mxl862xx_phy_read_mmd(bus->priv, port, 0, regnum); ++ return mxl862xx_phy_read_mmd(bus->priv, addr, 0, regnum); + } + +-static int mxl862xx_phy_write_mii_bus(struct mii_bus *bus, int port, ++static int mxl862xx_phy_write_mii_bus(struct mii_bus *bus, int addr, + int regnum, u16 val) + { +- return mxl862xx_phy_write_mmd(bus->priv, port, 0, regnum, val); ++ return mxl862xx_phy_write_mmd(bus->priv, addr, 0, regnum, val); + } + +-static int mxl862xx_phy_read_c45_mii_bus(struct mii_bus *bus, int port, ++static int mxl862xx_phy_read_c45_mii_bus(struct mii_bus *bus, int addr, + int devadd, int regnum) + { +- return mxl862xx_phy_read_mmd(bus->priv, port, devadd, regnum); ++ return mxl862xx_phy_read_mmd(bus->priv, addr, devadd, regnum); + } + +-static int mxl862xx_phy_write_c45_mii_bus(struct mii_bus *bus, int port, ++static int mxl862xx_phy_write_c45_mii_bus(struct mii_bus *bus, int addr, + int devadd, int regnum, u16 val) + { +- return mxl862xx_phy_write_mmd(bus->priv, port, devadd, regnum, val); ++ return mxl862xx_phy_write_mmd(bus->priv, addr, devadd, regnum, val); + } + + static int mxl862xx_wait_ready(struct dsa_switch *ds) diff --git a/target/linux/generic/backport-6.12/766-v7.0-net-dsa-mxl862xx-don-t-set-user_mii_bus.patch b/target/linux/generic/backport-6.12/766-v7.0-net-dsa-mxl862xx-don-t-set-user_mii_bus.patch new file mode 100644 index 00000000000..69c8ecc545c --- /dev/null +++ b/target/linux/generic/backport-6.12/766-v7.0-net-dsa-mxl862xx-don-t-set-user_mii_bus.patch @@ -0,0 +1,31 @@ +From d0341efa8f5182cafe16506b9bef98184f4951fe Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 10 Mar 2026 00:41:56 +0000 +Subject: [PATCH 05/35] net: dsa: mxl862xx: don't set user_mii_bus + +The PHY addresses in the MII bus are not equal to the port addresses, +so the bus cannot be assigned as user_mii_bus. Falling back on the +user_mii_bus in case a PHY isn't declared in device tree will result in +using the wrong (in this case: off-by-+1) PHY. +Remove the wrong assignment. + +Fixes: 23794bec1cb60 ("net: dsa: add basic initial driver for MxL862xx switches") +Suggested-by: Vladimir Oltean +Signed-off-by: Daniel Golle +Reviewed-by: Vladimir Oltean +Link: https://patch.msgid.link/0f0df310fd8cab57e0e5e3d0831dd057fd05bcd5.1773103271.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mxl862xx/mxl862xx.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -149,7 +149,6 @@ static int mxl862xx_setup_mdio(struct ds + return -ENOMEM; + + bus->priv = priv; +- ds->user_mii_bus = bus; + bus->name = KBUILD_MODNAME "-mii"; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev)); + bus->read_c45 = mxl862xx_phy_read_c45_mii_bus; diff --git a/target/linux/generic/backport-6.12/767-v7.0-net-dsa-MxL862xx-don-t-force-enable-MAXLINEAR_GPHY.patch b/target/linux/generic/backport-6.12/767-v7.0-net-dsa-MxL862xx-don-t-force-enable-MAXLINEAR_GPHY.patch new file mode 100644 index 00000000000..724a9d310a9 --- /dev/null +++ b/target/linux/generic/backport-6.12/767-v7.0-net-dsa-MxL862xx-don-t-force-enable-MAXLINEAR_GPHY.patch @@ -0,0 +1,54 @@ +From c0402837642625ef13ade862e20e229f4a5810f5 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 18 Mar 2026 03:07:52 +0000 +Subject: [PATCH 06/35] net: dsa: mxl862xx: don't read out-of-bounds + +The write loop in mxl862xx_api_wrap() computes the word count as +(size + 1) / 2, rounding up for odd-sized structs. + +On the last iteration of an odd-sized buffer it reads a full __le16 +from data[i], accessing one byte past the end of the caller's struct. +KASAN catches this as a stack-out-of-bounds read during probe (e.g. +from mxl862xx_bridge_config_fwd() because of the odd length of +sizeof(struct mxl862xx_bridge_config) == 49). + +The read-back loop already handles this case, it writes only a single +byte when (i * 2 + 1) == size. The write loop lacked the same guard. + +In practice the over-read is harmless: the extra stack byte is sent to +the firmware which ignores trailing data beyond the command's declared +payload size. + +Apply the same odd-size last-byte handling to the write path: when the +final word contains only one valid byte, send *(u8 *)&data[i] instead +of le16_to_cpu(data[i]). This is endian-safe because data is +__le16-encoded and the low byte is always at the lowest address +regardless of host byte order. + +Signed-off-by: Daniel Golle +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/83356ad9c9a4470dd49b6b3d661c2a8dd85cc6a1.1773803190.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mxl862xx/mxl862xx-host.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c +@@ -175,8 +175,14 @@ int mxl862xx_api_wrap(struct mxl862xx_pr + goto out; + } + +- ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_DATA_FIRST + off, +- le16_to_cpu(data[i])); ++ if ((i * 2 + 1) == size) ++ ret = mxl862xx_reg_write(priv, ++ MXL862XX_MMD_REG_DATA_FIRST + off, ++ *(u8 *)&data[i]); ++ else ++ ret = mxl862xx_reg_write(priv, ++ MXL862XX_MMD_REG_DATA_FIRST + off, ++ le16_to_cpu(data[i])); + if (ret < 0) + goto out; + } diff --git a/target/linux/generic/backport-6.12/768-v7.1-net-dsa-MxL862xx-don-t-force-enable-MAXLINEAR_GPHY.patch b/target/linux/generic/backport-6.12/768-v7.1-net-dsa-MxL862xx-don-t-force-enable-MAXLINEAR_GPHY.patch new file mode 100644 index 00000000000..a2252d34505 --- /dev/null +++ b/target/linux/generic/backport-6.12/768-v7.1-net-dsa-MxL862xx-don-t-force-enable-MAXLINEAR_GPHY.patch @@ -0,0 +1,39 @@ +From 06cdf1bf5ba80e90bc54e7fe0c096b47d5ab3d8d Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Mon, 16 Feb 2026 11:55:17 +0100 +Subject: [PATCH 07/35] net: dsa: MxL862xx: don't force-enable MAXLINEAR_GPHY + +The newly added dsa driver attempts to enable the corresponding PHY driver, +but that one has additional dependencies that may not be available: + +WARNING: unmet direct dependencies detected for MAXLINEAR_GPHY + Depends on [m]: NETDEVICES [=y] && PHYLIB [=y] && (HWMON [=m] || HWMON [=m]=n [=n]) + Selected by [y]: + - NET_DSA_MXL862 [=y] && NETDEVICES [=y] && NET_DSA [=y] +aarch64-linux-ld: drivers/net/phy/mxl-gpy.o: in function `gpy_probe': +mxl-gpy.c:(.text.gpy_probe+0x13c): undefined reference to `devm_hwmon_device_register_with_info' +aarch64-linux-ld: drivers/net/phy/mxl-gpy.o: in function `gpy_hwmon_read': +mxl-gpy.c:(.text.gpy_hwmon_read+0x48): undefined reference to `polynomial_calc' + +There is actually no compile-time dependency, as DSA correctly uses the +PHY abstractions. Remove the 'select' statement to reduce the complexity. + +Fixes: 23794bec1cb6 ("net: dsa: add basic initial driver for MxL862xx switches") +Signed-off-by: Arnd Bergmann +Reviewed-by: Daniel Golle +Link: https://patch.msgid.link/20260216105522.2382373-1-arnd@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mxl862xx/Kconfig | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/dsa/mxl862xx/Kconfig ++++ b/drivers/net/dsa/mxl862xx/Kconfig +@@ -2,7 +2,6 @@ + config NET_DSA_MXL862 + tristate "MaxLinear MxL862xx" + depends on NET_DSA +- select MAXLINEAR_GPHY + select NET_DSA_TAG_MXL_862XX + help + This enables support for the MaxLinear MxL862xx switch family. diff --git a/target/linux/generic/backport-6.12/769-v7.1-net-dsa-mxl862xx-add-CRC-for-MDIO-communication.patch b/target/linux/generic/backport-6.12/769-v7.1-net-dsa-mxl862xx-add-CRC-for-MDIO-communication.patch new file mode 100644 index 00000000000..8f65bea2ed5 --- /dev/null +++ b/target/linux/generic/backport-6.12/769-v7.1-net-dsa-mxl862xx-add-CRC-for-MDIO-communication.patch @@ -0,0 +1,585 @@ +From d48001906168be3088f9cd7aa8d1ad8dbc53e4f4 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 22 Mar 2026 13:27:20 +0000 +Subject: [PATCH 08/35] net: dsa: mxl862xx: add CRC for MDIO communication + +Enable the firmware's opt-in CRC validation on the MDIO/MMD command +interface to detect bit errors on the bus. The firmware bundles CRC-6 +and CRC-16 under a single enable flag, so both are implemented +together. + +CRC-6 protects the ctrl and len_ret command registers using a table- +driven 3GPP algorithm. It is applied to every command exchange +including SET_DATA/GET_DATA batch transfers. With CRC enabled, the +firmware encodes its return value as a signed 11-bit integer within +the CRC- protected register fields, replacing the previous 16-bit +interpretation. + +CRC-16 protects the data payload using the kernel's crc16() library. +The driver appends a CRC-16 checksum to outgoing data and verifies the +firmware-appended checksum on responses. The checksum is placed at the +exact byte offset where the struct data ends, correctly handling +packed structs with odd sizes by splitting the checksum across word +boundaries. SET_DATA/GET_DATA sub-commands carry only CRC-6. + +Upon detection of a CRC error on either side all conduit interfaces +are taken down, triggering all user ports to go down as well. This is +the most feasible option: CRC errors are likely caused either by +broken hardware, or are symptom of overheating. In either case, trying +to resume normal operation isn't reasonable. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/620453b9a150bbe5b7ea4224331cb5dc5e57263b.1774185953.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mxl862xx/Kconfig | 1 + + drivers/net/dsa/mxl862xx/mxl862xx-host.c | 345 ++++++++++++++++++----- + drivers/net/dsa/mxl862xx/mxl862xx-host.h | 2 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 11 + + drivers/net/dsa/mxl862xx/mxl862xx.h | 2 + + 5 files changed, 296 insertions(+), 65 deletions(-) + +--- a/drivers/net/dsa/mxl862xx/Kconfig ++++ b/drivers/net/dsa/mxl862xx/Kconfig +@@ -2,6 +2,7 @@ + config NET_DSA_MXL862 + tristate "MaxLinear MxL862xx" + depends on NET_DSA ++ select CRC16 + select NET_DSA_TAG_MXL_862XX + help + This enables support for the MaxLinear MxL862xx switch family. +--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c +@@ -7,7 +7,9 @@ + * Copyright (C) 2024 MaxLinear Inc. + */ + ++#include + #include ++#include + #include + #include + #include +@@ -15,6 +17,9 @@ + #include "mxl862xx-host.h" + + #define CTRL_BUSY_MASK BIT(15) ++#define CTRL_CRC_FLAG BIT(14) ++ ++#define LEN_RET_LEN_MASK GENMASK(9, 0) + + #define MXL862XX_MMD_REG_CTRL 0 + #define MXL862XX_MMD_REG_LEN_RET 1 +@@ -27,7 +32,159 @@ + #define MMD_API_GET_DATA_0 5 + #define MMD_API_RST_DATA 8 + +-#define MXL862XX_SWITCH_RESET 0x9907 ++#define MXL862XX_SWITCH_RESET 0x9907 ++ ++static void mxl862xx_crc_err_work_fn(struct work_struct *work) ++{ ++ struct mxl862xx_priv *priv = container_of(work, struct mxl862xx_priv, ++ crc_err_work); ++ struct dsa_port *dp; ++ ++ dev_warn(&priv->mdiodev->dev, ++ "MDIO CRC error detected, shutting down all ports\n"); ++ ++ rtnl_lock(); ++ dsa_switch_for_each_cpu_port(dp, priv->ds) ++ dev_close(dp->conduit); ++ rtnl_unlock(); ++ ++ clear_bit(0, &priv->crc_err); ++} ++ ++/* Firmware CRC error codes (outside normal Zephyr errno range). */ ++#define MXL862XX_FW_CRC6_ERR (-1024) ++#define MXL862XX_FW_CRC16_ERR (-1023) ++ ++/* 3GPP CRC-6 lookup table (polynomial 0x6F). ++ * Matches the firmware's default CRC-6 implementation. ++ */ ++static const u8 mxl862xx_crc6_table[256] = { ++ 0x00, 0x2f, 0x31, 0x1e, 0x0d, 0x22, 0x3c, 0x13, ++ 0x1a, 0x35, 0x2b, 0x04, 0x17, 0x38, 0x26, 0x09, ++ 0x34, 0x1b, 0x05, 0x2a, 0x39, 0x16, 0x08, 0x27, ++ 0x2e, 0x01, 0x1f, 0x30, 0x23, 0x0c, 0x12, 0x3d, ++ 0x07, 0x28, 0x36, 0x19, 0x0a, 0x25, 0x3b, 0x14, ++ 0x1d, 0x32, 0x2c, 0x03, 0x10, 0x3f, 0x21, 0x0e, ++ 0x33, 0x1c, 0x02, 0x2d, 0x3e, 0x11, 0x0f, 0x20, ++ 0x29, 0x06, 0x18, 0x37, 0x24, 0x0b, 0x15, 0x3a, ++ 0x0e, 0x21, 0x3f, 0x10, 0x03, 0x2c, 0x32, 0x1d, ++ 0x14, 0x3b, 0x25, 0x0a, 0x19, 0x36, 0x28, 0x07, ++ 0x3a, 0x15, 0x0b, 0x24, 0x37, 0x18, 0x06, 0x29, ++ 0x20, 0x0f, 0x11, 0x3e, 0x2d, 0x02, 0x1c, 0x33, ++ 0x09, 0x26, 0x38, 0x17, 0x04, 0x2b, 0x35, 0x1a, ++ 0x13, 0x3c, 0x22, 0x0d, 0x1e, 0x31, 0x2f, 0x00, ++ 0x3d, 0x12, 0x0c, 0x23, 0x30, 0x1f, 0x01, 0x2e, ++ 0x27, 0x08, 0x16, 0x39, 0x2a, 0x05, 0x1b, 0x34, ++ 0x1c, 0x33, 0x2d, 0x02, 0x11, 0x3e, 0x20, 0x0f, ++ 0x06, 0x29, 0x37, 0x18, 0x0b, 0x24, 0x3a, 0x15, ++ 0x28, 0x07, 0x19, 0x36, 0x25, 0x0a, 0x14, 0x3b, ++ 0x32, 0x1d, 0x03, 0x2c, 0x3f, 0x10, 0x0e, 0x21, ++ 0x1b, 0x34, 0x2a, 0x05, 0x16, 0x39, 0x27, 0x08, ++ 0x01, 0x2e, 0x30, 0x1f, 0x0c, 0x23, 0x3d, 0x12, ++ 0x2f, 0x00, 0x1e, 0x31, 0x22, 0x0d, 0x13, 0x3c, ++ 0x35, 0x1a, 0x04, 0x2b, 0x38, 0x17, 0x09, 0x26, ++ 0x12, 0x3d, 0x23, 0x0c, 0x1f, 0x30, 0x2e, 0x01, ++ 0x08, 0x27, 0x39, 0x16, 0x05, 0x2a, 0x34, 0x1b, ++ 0x26, 0x09, 0x17, 0x38, 0x2b, 0x04, 0x1a, 0x35, ++ 0x3c, 0x13, 0x0d, 0x22, 0x31, 0x1e, 0x00, 0x2f, ++ 0x15, 0x3a, 0x24, 0x0b, 0x18, 0x37, 0x29, 0x06, ++ 0x0f, 0x20, 0x3e, 0x11, 0x02, 0x2d, 0x33, 0x1c, ++ 0x21, 0x0e, 0x10, 0x3f, 0x2c, 0x03, 0x1d, 0x32, ++ 0x3b, 0x14, 0x0a, 0x25, 0x36, 0x19, 0x07, 0x28, ++}; ++ ++/* Compute 3GPP CRC-6 over the ctrl register (16 bits) and the lower ++ * 10 bits of the len_ret register. The 26-bit input is packed as ++ * { len_ret[9:0], ctrl[15:0] } and processed LSB-first through the ++ * lookup table. ++ */ ++static u8 mxl862xx_crc6(u16 ctrl, u16 len_ret) ++{ ++ u32 data = ((u32)(len_ret & LEN_RET_LEN_MASK) << 16) | ctrl; ++ u8 crc = 0; ++ int i; ++ ++ for (i = 0; i < sizeof(data); i++, data >>= 8) ++ crc = mxl862xx_crc6_table[(crc << 2) ^ (data & 0xff)] & 0x3f; ++ ++ return crc; ++} ++ ++/* Encode CRC-6 into the ctrl and len_ret registers before writing them ++ * to MDIO. The caller must set ctrl = API_ID | CTRL_BUSY_MASK | ++ * CTRL_CRC_FLAG, and len_ret = parameter length (bits 0-9 only). ++ * ++ * After encoding: ++ * ctrl[12:0] = API ID (unchanged) ++ * ctrl[14:13] = CRC-6 bits 5-4 ++ * ctrl[15] = busy flag (unchanged) ++ * len_ret[9:0] = parameter length (unchanged) ++ * len_ret[13:10] = CRC-6 bits 3-0 ++ * len_ret[14] = original ctrl[14] (CRC check flag, forwarded to FW) ++ * len_ret[15] = original ctrl[13] (magic bit, always 1) ++ */ ++static void mxl862xx_crc6_encode(u16 *pctrl, u16 *plen_ret) ++{ ++ u16 crc, ctrl, len_ret; ++ ++ /* Set magic bit before CRC computation */ ++ *pctrl |= BIT(13); ++ ++ crc = mxl862xx_crc6(*pctrl, *plen_ret); ++ ++ /* Place CRC MSB (bits 5-4) into ctrl bits 13-14 */ ++ ctrl = (*pctrl & ~GENMASK(14, 13)); ++ ctrl |= (crc & 0x30) << 9; ++ ++ /* Place CRC LSB (bits 3-0) into len_ret bits 10-13 */ ++ len_ret = *plen_ret | ((crc & 0x0f) << 10); ++ ++ /* Forward ctrl[14] (CRC check flag) to len_ret[14], ++ * and ctrl[13] (magic, always 1) to len_ret[15]. ++ */ ++ len_ret |= (*pctrl & BIT(14)) | ((*pctrl & BIT(13)) << 2); ++ ++ *pctrl = ctrl; ++ *plen_ret = len_ret; ++} ++ ++/* Verify CRC-6 on a firmware response and extract the return value. ++ * ++ * The firmware encodes the return value as a signed 11-bit integer: ++ * - Sign bit (bit 10) in ctrl[14] ++ * - Magnitude (bits 9-0) in len_ret[9:0] ++ * These are recoverable after CRC-6 verification by restoring the ++ * original ctrl from the auxiliary copies in len_ret[15:14]. ++ * ++ * Return: 0 on CRC match (with *result set), or -EIO on mismatch. ++ */ ++static int mxl862xx_crc6_verify(u16 ctrl, u16 len_ret, int *result) ++{ ++ u16 crc_recv, crc_calc; ++ ++ /* Extract the received CRC-6 */ ++ crc_recv = ((ctrl >> 9) & 0x30) | ((len_ret >> 10) & 0x0f); ++ ++ /* Reconstruct the original ctrl for re-computation: ++ * ctrl[14] = len_ret[14] (sign bit / CRC check flag) ++ * ctrl[13] = len_ret[15] >> 2 (magic bit) ++ */ ++ ctrl &= ~GENMASK(14, 13); ++ ctrl |= len_ret & BIT(14); ++ ctrl |= (len_ret & BIT(15)) >> 2; ++ ++ crc_calc = mxl862xx_crc6(ctrl, len_ret); ++ if (crc_recv != crc_calc) ++ return -EIO; ++ ++ /* Extract signed 11-bit return value: ++ * bit 10 (sign) from ctrl[14], bits 9-0 from len_ret[9:0] ++ */ ++ *result = sign_extend32((len_ret & LEN_RET_LEN_MASK) | ++ ((ctrl & CTRL_CRC_FLAG) >> 4), 10); ++ ++ return 0; ++} + + static int mxl862xx_reg_read(struct mxl862xx_priv *priv, u32 addr) + { +@@ -52,60 +209,78 @@ static int mxl862xx_busy_wait(struct mxl + !(val & CTRL_BUSY_MASK), 15, 500000); + } + +-static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words) ++/* Issue a firmware command with CRC-6 protection on the ctrl and len_ret ++ * registers, wait for completion, and verify the response CRC-6. ++ * ++ * Return: firmware result value (>= 0) on success, or negative errno. ++ */ ++static int mxl862xx_issue_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 len) + { +- int ret; +- u16 cmd; ++ u16 ctrl_enc, len_enc; ++ int ret, fw_result; ++ ++ ctrl_enc = cmd | CTRL_BUSY_MASK | CTRL_CRC_FLAG; ++ len_enc = len; ++ mxl862xx_crc6_encode(&ctrl_enc, &len_enc); + +- ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, +- MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, len_enc); ++ if (ret < 0) ++ return ret; ++ ++ ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, ctrl_enc); ++ if (ret < 0) ++ return ret; ++ ++ ret = mxl862xx_busy_wait(priv); ++ if (ret < 0) ++ return ret; ++ ++ ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL); ++ if (ret < 0) ++ return ret; ++ ctrl_enc = ret; ++ ++ ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET); + if (ret < 0) + return ret; ++ len_enc = ret; ++ ++ ret = mxl862xx_crc6_verify(ctrl_enc, len_enc, &fw_result); ++ if (ret) { ++ if (!test_and_set_bit(0, &priv->crc_err)) ++ schedule_work(&priv->crc_err_work); ++ return -EIO; ++ } ++ ++ return fw_result; ++} ++ ++static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words) ++{ ++ u16 cmd; + + cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1; + if (!(cmd < 2)) + return -EINVAL; + + cmd += MMD_API_SET_DATA_0; +- ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, +- cmd | CTRL_BUSY_MASK); +- if (ret < 0) +- return ret; + +- return mxl862xx_busy_wait(priv); ++ return mxl862xx_issue_cmd(priv, cmd, ++ MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); + } + + static int mxl862xx_get_data(struct mxl862xx_priv *priv, u16 words) + { +- int ret; + u16 cmd; + +- ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, +- MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); +- if (ret < 0) +- return ret; +- + cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE; + if (!(cmd > 0 && cmd < 3)) + return -EINVAL; + + cmd += MMD_API_GET_DATA_0; +- ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, +- cmd | CTRL_BUSY_MASK); +- if (ret < 0) +- return ret; +- +- return mxl862xx_busy_wait(priv); +-} +- +-static int mxl862xx_firmware_return(int ret) +-{ +- /* Only 16-bit values are valid. */ +- if (WARN_ON(ret & GENMASK(31, 16))) +- return -EINVAL; + +- /* Interpret value as signed 16-bit integer. */ +- return (s16)ret; ++ return mxl862xx_issue_cmd(priv, cmd, ++ MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); + } + + static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size, +@@ -113,30 +288,23 @@ static int mxl862xx_send_cmd(struct mxl8 + { + int ret; + +- ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, size); +- if (ret) +- return ret; ++ ret = mxl862xx_issue_cmd(priv, cmd, size); + +- ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, +- cmd | CTRL_BUSY_MASK); +- if (ret) +- return ret; +- +- ret = mxl862xx_busy_wait(priv); +- if (ret) +- return ret; +- +- ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET); +- if (ret < 0) +- return ret; +- +- /* handle errors returned by the firmware as -EIO ++ /* Handle errors returned by the firmware as -EIO. + * The firmware is based on Zephyr OS and uses the errors as + * defined in errno.h of Zephyr OS. See + * https://github.com/zephyrproject-rtos/zephyr/blob/v3.7.0/lib/libc/minimal/include/errno.h ++ * ++ * The firmware signals CRC validation failures with dedicated ++ * error codes outside the normal Zephyr errno range: ++ * -1024: CRC-6 mismatch on ctrl/len_ret registers ++ * -1023: CRC-16 mismatch on data payload + */ +- ret = mxl862xx_firmware_return(ret); + if (ret < 0) { ++ if ((ret == MXL862XX_FW_CRC6_ERR || ++ ret == MXL862XX_FW_CRC16_ERR) && ++ !test_and_set_bit(0, &priv->crc_err)) ++ schedule_work(&priv->crc_err_work); + if (!quiet) + dev_err(&priv->mdiodev->dev, + "CMD %04x returned error %d\n", cmd, ret); +@@ -151,7 +319,7 @@ int mxl862xx_api_wrap(struct mxl862xx_pr + { + __le16 *data = _data; + int ret, cmd_ret; +- u16 max, i; ++ u16 max, crc, i; + + dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data); + +@@ -163,26 +331,45 @@ int mxl862xx_api_wrap(struct mxl862xx_pr + if (ret < 0) + goto out; + +- for (i = 0; i < max; i++) { ++ /* Compute CRC-16 over the data payload; written as an extra word ++ * after the data so the firmware can verify the transfer. ++ */ ++ crc = crc16(0xffff, (const u8 *)data, size); ++ ++ for (i = 0; i < max + 1; i++) { + u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; ++ u16 val; + + if (i && off == 0) { + /* Send command to set data when every + * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written. +- */ ++ */ + ret = mxl862xx_set_data(priv, i); + if (ret < 0) + goto out; + } + +- if ((i * 2 + 1) == size) +- ret = mxl862xx_reg_write(priv, +- MXL862XX_MMD_REG_DATA_FIRST + off, +- *(u8 *)&data[i]); +- else +- ret = mxl862xx_reg_write(priv, +- MXL862XX_MMD_REG_DATA_FIRST + off, +- le16_to_cpu(data[i])); ++ if (i == max) { ++ /* Even size: full CRC word. ++ * Odd size: only CRC high byte remains (low byte ++ * was packed into the previous word). ++ */ ++ val = (size & 1) ? crc >> 8 : crc; ++ } else if ((i * 2 + 1) == size) { ++ /* Special handling for last BYTE if it's not WORD ++ * aligned to avoid reading beyond the allocated data ++ * structure. Pack the CRC low byte into the high ++ * byte of this word so it sits at byte offset 'size' ++ * in the firmware's contiguous buffer. ++ */ ++ val = *(u8 *)&data[i] | ((crc & 0xff) << 8); ++ } else { ++ val = le16_to_cpu(data[i]); ++ } ++ ++ ret = mxl862xx_reg_write(priv, ++ MXL862XX_MMD_REG_DATA_FIRST + off, ++ val); + if (ret < 0) + goto out; + } +@@ -194,13 +381,13 @@ int mxl862xx_api_wrap(struct mxl862xx_pr + /* store result of mxl862xx_send_cmd() */ + cmd_ret = ret; + +- for (i = 0; i < max; i++) { ++ for (i = 0; i < max + 1; i++) { + u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; + + if (i && off == 0) { + /* Send command to fetch next batch of data when every + * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are read. +- */ ++ */ + ret = mxl862xx_get_data(priv, i); + if (ret < 0) + goto out; +@@ -210,17 +397,35 @@ int mxl862xx_api_wrap(struct mxl862xx_pr + if (ret < 0) + goto out; + +- if ((i * 2 + 1) == size) { ++ if (i == max) { ++ /* Even size: full CRC word. ++ * Odd size: only CRC high byte remains (low byte ++ * was in the previous word). ++ */ ++ if (size & 1) ++ crc = (crc & 0x00ff) | ++ (((u16)ret & 0xff) << 8); ++ else ++ crc = (u16)ret; ++ } else if ((i * 2 + 1) == size) { + /* Special handling for last BYTE if it's not WORD + * aligned to avoid writing beyond the allocated data +- * structure. ++ * structure. The high byte carries the CRC low byte. + */ + *(uint8_t *)&data[i] = ret & 0xff; ++ crc = (ret >> 8) & 0xff; + } else { + data[i] = cpu_to_le16((u16)ret); + } + } + ++ if (crc16(0xffff, (const u8 *)data, size) != crc) { ++ if (!test_and_set_bit(0, &priv->crc_err)) ++ schedule_work(&priv->crc_err_work); ++ ret = -EIO; ++ goto out; ++ } ++ + /* on success return the result of the mxl862xx_send_cmd() */ + ret = cmd_ret; + +@@ -249,3 +454,13 @@ out: + + return ret; + } ++ ++void mxl862xx_host_init(struct mxl862xx_priv *priv) ++{ ++ INIT_WORK(&priv->crc_err_work, mxl862xx_crc_err_work_fn); ++} ++ ++void mxl862xx_host_shutdown(struct mxl862xx_priv *priv) ++{ ++ cancel_work_sync(&priv->crc_err_work); ++} +--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.h +@@ -5,6 +5,8 @@ + + #include "mxl862xx.h" + ++void mxl862xx_host_init(struct mxl862xx_priv *priv); ++void mxl862xx_host_shutdown(struct mxl862xx_priv *priv); + int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *data, u16 size, + bool read, bool quiet); + int mxl862xx_reset(struct mxl862xx_priv *priv); +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -424,6 +424,7 @@ static int mxl862xx_probe(struct mdio_de + ds->ops = &mxl862xx_switch_ops; + ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops; + ds->num_ports = MXL862XX_MAX_PORTS; ++ mxl862xx_host_init(priv); + + dev_set_drvdata(dev, ds); + +@@ -433,22 +434,32 @@ static int mxl862xx_probe(struct mdio_de + static void mxl862xx_remove(struct mdio_device *mdiodev) + { + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); ++ struct mxl862xx_priv *priv; + + if (!ds) + return; + ++ priv = ds->priv; ++ + dsa_unregister_switch(ds); ++ ++ mxl862xx_host_shutdown(priv); + } + + static void mxl862xx_shutdown(struct mdio_device *mdiodev) + { + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); ++ struct mxl862xx_priv *priv; + + if (!ds) + return; + ++ priv = ds->priv; ++ + dsa_switch_shutdown(ds); + ++ mxl862xx_host_shutdown(priv); ++ + dev_set_drvdata(&mdiodev->dev, NULL); + } + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -11,6 +11,8 @@ + struct mxl862xx_priv { + struct dsa_switch *ds; + struct mdio_device *mdiodev; ++ struct work_struct crc_err_work; ++ unsigned long crc_err; + }; + + #endif /* __MXL862XX_H */ diff --git a/target/linux/generic/backport-6.12/770-v7.1-net-dsa-mxl862xx-use-RST_DATA-to-skip-writing-zero-w.patch b/target/linux/generic/backport-6.12/770-v7.1-net-dsa-mxl862xx-use-RST_DATA-to-skip-writing-zero-w.patch new file mode 100644 index 00000000000..dc21f10cadc --- /dev/null +++ b/target/linux/generic/backport-6.12/770-v7.1-net-dsa-mxl862xx-use-RST_DATA-to-skip-writing-zero-w.patch @@ -0,0 +1,93 @@ +From 4a296a038c0ea3ad20afe8df00eb083232317646 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 22 Mar 2026 13:27:26 +0000 +Subject: [PATCH 09/35] net: dsa: mxl862xx: use RST_DATA to skip writing zero + words + +Issue the firmware's RST_DATA command before writing data payloads that +contain many zero words. RST_DATA zeroes both the firmware's internal +buffer and the MMD data registers in a single command, allowing the +driver to skip individual MDIO writes for zero-valued words. This +reduces bus traffic for the common case where API structs have many +unused or default-zero fields. + +The optimization is applied when at least 5 zero words are found in the +payload, roughly the break-even point against the cost of the extra +RST_DATA command round-trip. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/d10bd6ad5df062d0da342c3e0d330550b3d2432b.1774185953.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mxl862xx/mxl862xx-host.c | 38 ++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c +@@ -283,6 +283,17 @@ static int mxl862xx_get_data(struct mxl8 + MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); + } + ++static int mxl862xx_rst_data(struct mxl862xx_priv *priv) ++{ ++ return mxl862xx_issue_cmd(priv, MMD_API_RST_DATA, 0); ++} ++ ++/* Minimum number of zero words in the data payload before issuing a ++ * RST_DATA command is worthwhile. RST_DATA costs one full command ++ * round-trip (~5 MDIO transactions), so the threshold must offset that. ++ */ ++#define RST_DATA_THRESHOLD 5 ++ + static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size, + bool quiet) + { +@@ -318,6 +329,8 @@ int mxl862xx_api_wrap(struct mxl862xx_pr + u16 size, bool read, bool quiet) + { + __le16 *data = _data; ++ bool use_rst = false; ++ unsigned int zeros; + int ret, cmd_ret; + u16 max, crc, i; + +@@ -331,6 +344,24 @@ int mxl862xx_api_wrap(struct mxl862xx_pr + if (ret < 0) + goto out; + ++ /* If the data contains enough zero words, issue RST_DATA to zero ++ * both the firmware buffer and MMD registers, then skip writing ++ * zero words individually. ++ */ ++ for (i = 0, zeros = 0; i < size / 2 && zeros < RST_DATA_THRESHOLD; i++) ++ if (!data[i]) ++ zeros++; ++ ++ if (zeros < RST_DATA_THRESHOLD && (size & 1) && !*(u8 *)&data[i]) ++ zeros++; ++ ++ if (zeros >= RST_DATA_THRESHOLD) { ++ ret = mxl862xx_rst_data(priv); ++ if (ret < 0) ++ goto out; ++ use_rst = true; ++ } ++ + /* Compute CRC-16 over the data payload; written as an extra word + * after the data so the firmware can verify the transfer. + */ +@@ -367,6 +398,13 @@ int mxl862xx_api_wrap(struct mxl862xx_pr + val = le16_to_cpu(data[i]); + } + ++ /* After RST_DATA, skip zero data words as the registers ++ * already contain zeros, but never skip the CRC word at the ++ * final word. ++ */ ++ if (use_rst && i < max && val == 0) ++ continue; ++ + ret = mxl862xx_reg_write(priv, + MXL862XX_MMD_REG_DATA_FIRST + off, + val); diff --git a/target/linux/generic/backport-6.12/780-01-v6.13-r8169-remove-original-workaround-for-RTL8125-broken-.patch b/target/linux/generic/backport-6.12/780-01-v6.13-r8169-remove-original-workaround-for-RTL8125-broken-.patch new file mode 100644 index 00000000000..bd10a967722 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-01-v6.13-r8169-remove-original-workaround-for-RTL8125-broken-.patch @@ -0,0 +1,33 @@ +From 854d71c555dfc3383c1fde7d9989b6046e21093d Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 9 Oct 2024 07:48:05 +0200 +Subject: [PATCH] r8169: remove original workaround for RTL8125 broken rx issue + +Now that we have b9c7ac4fe22c ("r8169: disable ALDPS per default for +RTL8125"), the first attempt to fix the issue shouldn't be needed +any longer. So let's effectively revert 621735f59064 ("r8169: fix +rare issue with broken rx after link-down on RTL8125") and see +whether anybody complains. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/382d8c88-cbce-400f-ad62-fda0181c7e38@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -4810,11 +4810,7 @@ static void r8169_phylink_handler(struct + if (netif_carrier_ok(ndev)) { + rtl_link_chg_patch(tp); + pm_request_resume(d); +- netif_wake_queue(tp->dev); + } else { +- /* In few cases rx is broken after link-down otherwise */ +- if (rtl_is_8125(tp)) +- rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_NO_QUEUE_WAKE); + pm_runtime_idle(d); + } + diff --git a/target/linux/generic/backport-6.12/780-02-v6.13-r8169-enable-SG-TSO-on-selected-chip-versions-per-de.patch b/target/linux/generic/backport-6.12/780-02-v6.13-r8169-enable-SG-TSO-on-selected-chip-versions-per-de.patch new file mode 100644 index 00000000000..7ea1a11ee35 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-02-v6.13-r8169-enable-SG-TSO-on-selected-chip-versions-per-de.patch @@ -0,0 +1,52 @@ +From b8bf38440ba94e8ed8e2ae55c5dfb0276d30e843 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Thu, 10 Oct 2024 12:58:02 +0200 +Subject: [PATCH] r8169: enable SG/TSO on selected chip versions per default + +Due to problem reports in the past SG and TSO/TSO6 are disabled per +default. It's not fully clear which chip versions are affected, so we +may impact also users of unaffected chip versions, unless they know +how to use ethtool for enabling SG/TSO/TSO6. +Vendor drivers r8168/r8125 enable SG/TSO/TSO6 for selected chip +versions per default, I'd interpret this as confirmation that these +chip versions are unaffected. So let's do the same here. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/realtek/r8169_main.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -5524,11 +5524,6 @@ static int rtl_init_one(struct pci_dev * + + dev->features |= dev->hw_features; + +- /* There has been a number of reports that using SG/TSO results in +- * tx timeouts. However for a lot of people SG/TSO works fine. +- * Therefore disable both features by default, but allow users to +- * enable them. Use at own risk! +- */ + if (rtl_chip_supports_csum_v2(tp)) { + dev->hw_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6; + netif_set_tso_max_size(dev, RTL_GSO_MAX_SIZE_V2); +@@ -5539,6 +5534,17 @@ static int rtl_init_one(struct pci_dev * + netif_set_tso_max_segs(dev, RTL_GSO_MAX_SEGS_V1); + } + ++ /* There has been a number of reports that using SG/TSO results in ++ * tx timeouts. However for a lot of people SG/TSO works fine. ++ * It's not fully clear which chip versions are affected. Vendor ++ * drivers enable SG/TSO for certain chip versions per default, ++ * let's mimic this here. On other chip versions users can ++ * use ethtool to enable SG/TSO, use at own risk! ++ */ ++ if (tp->mac_version >= RTL_GIGA_MAC_VER_46 && ++ tp->mac_version != RTL_GIGA_MAC_VER_61) ++ dev->features |= dev->hw_features; ++ + dev->hw_features |= NETIF_F_RXALL; + dev->hw_features |= NETIF_F_RXFCS; + diff --git a/target/linux/generic/backport-6.12/780-03-v6.13-r8169-implement-additional-ethtool-stats-ops.patch b/target/linux/generic/backport-6.12/780-03-v6.13-r8169-implement-additional-ethtool-stats-ops.patch new file mode 100644 index 00000000000..594a8fae1a0 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-03-v6.13-r8169-implement-additional-ethtool-stats-ops.patch @@ -0,0 +1,130 @@ +From e3fc5139bd8ffaa1498adc21be4e8ecbc6aed508 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Sun, 13 Oct 2024 11:17:39 +0200 +Subject: [PATCH] r8169: implement additional ethtool stats ops + +This adds support for ethtool standard statistics, and makes use of the +extended hardware statistics being available from RTl8125. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/58e0da73-a7dd-4be3-82ae-d5b3f9069bde@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 82 +++++++++++++++++++++++ + 1 file changed, 82 insertions(+) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -2160,6 +2160,19 @@ static void rtl8169_get_ringparam(struct + data->tx_pending = NUM_TX_DESC; + } + ++static void rtl8169_get_pause_stats(struct net_device *dev, ++ struct ethtool_pause_stats *pause_stats) ++{ ++ struct rtl8169_private *tp = netdev_priv(dev); ++ ++ if (!rtl_is_8125(tp)) ++ return; ++ ++ rtl8169_update_counters(tp); ++ pause_stats->tx_pause_frames = le32_to_cpu(tp->counters->tx_pause_on); ++ pause_stats->rx_pause_frames = le32_to_cpu(tp->counters->rx_pause_on); ++} ++ + static void rtl8169_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *data) + { +@@ -2186,6 +2199,69 @@ static int rtl8169_set_pauseparam(struct + return 0; + } + ++static void rtl8169_get_eth_mac_stats(struct net_device *dev, ++ struct ethtool_eth_mac_stats *mac_stats) ++{ ++ struct rtl8169_private *tp = netdev_priv(dev); ++ ++ rtl8169_update_counters(tp); ++ ++ mac_stats->FramesTransmittedOK = ++ le64_to_cpu(tp->counters->tx_packets); ++ mac_stats->SingleCollisionFrames = ++ le32_to_cpu(tp->counters->tx_one_collision); ++ mac_stats->MultipleCollisionFrames = ++ le32_to_cpu(tp->counters->tx_multi_collision); ++ mac_stats->FramesReceivedOK = ++ le64_to_cpu(tp->counters->rx_packets); ++ mac_stats->AlignmentErrors = ++ le16_to_cpu(tp->counters->align_errors); ++ mac_stats->FramesLostDueToIntMACXmitError = ++ le64_to_cpu(tp->counters->tx_errors); ++ mac_stats->BroadcastFramesReceivedOK = ++ le64_to_cpu(tp->counters->rx_broadcast); ++ mac_stats->MulticastFramesReceivedOK = ++ le32_to_cpu(tp->counters->rx_multicast); ++ ++ if (!rtl_is_8125(tp)) ++ return; ++ ++ mac_stats->AlignmentErrors = ++ le32_to_cpu(tp->counters->align_errors32); ++ mac_stats->OctetsTransmittedOK = ++ le64_to_cpu(tp->counters->tx_octets); ++ mac_stats->LateCollisions = ++ le32_to_cpu(tp->counters->tx_late_collision); ++ mac_stats->FramesAbortedDueToXSColls = ++ le32_to_cpu(tp->counters->tx_aborted32); ++ mac_stats->OctetsReceivedOK = ++ le64_to_cpu(tp->counters->rx_octets); ++ mac_stats->FramesLostDueToIntMACRcvError = ++ le32_to_cpu(tp->counters->rx_mac_error); ++ mac_stats->MulticastFramesXmittedOK = ++ le64_to_cpu(tp->counters->tx_multicast64); ++ mac_stats->BroadcastFramesXmittedOK = ++ le64_to_cpu(tp->counters->tx_broadcast64); ++ mac_stats->MulticastFramesReceivedOK = ++ le64_to_cpu(tp->counters->rx_multicast64); ++ mac_stats->FrameTooLongErrors = ++ le32_to_cpu(tp->counters->rx_frame_too_long); ++} ++ ++static void rtl8169_get_eth_ctrl_stats(struct net_device *dev, ++ struct ethtool_eth_ctrl_stats *ctrl_stats) ++{ ++ struct rtl8169_private *tp = netdev_priv(dev); ++ ++ if (!rtl_is_8125(tp)) ++ return; ++ ++ rtl8169_update_counters(tp); ++ ++ ctrl_stats->UnsupportedOpcodesReceived = ++ le32_to_cpu(tp->counters->rx_unknown_opcode); ++} ++ + static const struct ethtool_ops rtl8169_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_MAX_FRAMES, +@@ -2207,8 +2283,11 @@ static const struct ethtool_ops rtl8169_ + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_ringparam = rtl8169_get_ringparam, ++ .get_pause_stats = rtl8169_get_pause_stats, + .get_pauseparam = rtl8169_get_pauseparam, + .set_pauseparam = rtl8169_set_pauseparam, ++ .get_eth_mac_stats = rtl8169_get_eth_mac_stats, ++ .get_eth_ctrl_stats = rtl8169_get_eth_ctrl_stats, + }; + + static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) +@@ -3926,6 +4005,9 @@ static void rtl_hw_start_8125(struct rtl + break; + } + ++ /* enable extended tally counter */ ++ r8168_mac_ocp_modify(tp, 0xea84, 0, BIT(1) | BIT(0)); ++ + rtl_hw_config(tp); + } + diff --git a/target/linux/generic/backport-6.12/780-04-v6.13-r8169-don-t-take-RTNL-lock-in-rtl_task.patch b/target/linux/generic/backport-6.12/780-04-v6.13-r8169-don-t-take-RTNL-lock-in-rtl_task.patch new file mode 100644 index 00000000000..a125ebb6cdc --- /dev/null +++ b/target/linux/generic/backport-6.12/780-04-v6.13-r8169-don-t-take-RTNL-lock-in-rtl_task.patch @@ -0,0 +1,50 @@ +From ac48430368c1a4f4e6c2fa92243b4b93fd25bee4 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 16 Oct 2024 22:05:57 +0200 +Subject: [PATCH] r8169: don't take RTNL lock in rtl_task() + +There's not really a benefit here in taking the RTNL lock. The task +handler does exception handling only, so we're in trouble anyway when +we come here, and there's no need to protect against e.g. a parallel +ethtool call. +A benefit of removing the RTNL lock here is that we now can +synchronously cancel the workqueue from a context holding the RTNL mutex. + +Signed-off-by: Heiner Kallweit +Signed-off-by: Andrew Lunn +--- + drivers/net/ethernet/realtek/r8169_main.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -4833,10 +4833,8 @@ static void rtl_task(struct work_struct + container_of(work, struct rtl8169_private, wk.work); + int ret; + +- rtnl_lock(); +- + if (!test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags)) +- goto out_unlock; ++ return; + + if (test_and_clear_bit(RTL_FLAG_TASK_TX_TIMEOUT, tp->wk.flags)) { + /* if chip isn't accessible, reset bus to revive it */ +@@ -4845,7 +4843,7 @@ static void rtl_task(struct work_struct + if (ret < 0) { + netdev_err(tp->dev, "Can't reset secondary PCI bus, detach NIC\n"); + netif_device_detach(tp->dev); +- goto out_unlock; ++ return; + } + } + +@@ -4864,8 +4862,6 @@ reset: + } else if (test_and_clear_bit(RTL_FLAG_TASK_RESET_NO_QUEUE_WAKE, tp->wk.flags)) { + rtl_reset_work(tp); + } +-out_unlock: +- rtnl_unlock(); + } + + static int rtl8169_poll(struct napi_struct *napi, int budget) diff --git a/target/linux/generic/backport-6.12/780-05-v6.13-replace-custom-flag-with-disable_work-et-al.patch b/target/linux/generic/backport-6.12/780-05-v6.13-replace-custom-flag-with-disable_work-et-al.patch new file mode 100644 index 00000000000..49e274a5e25 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-05-v6.13-replace-custom-flag-with-disable_work-et-al.patch @@ -0,0 +1,94 @@ +From e2015942e90a021151a5751776f35830ba063be7 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 16 Oct 2024 22:06:53 +0200 +Subject: [PATCH] r8169: replace custom flag with disable_work() et al + +So far we use a custom flag to define when a task can be scheduled and +when not. Let's use the standard mechanism with disable_work() et al +instead. +Note that in rtl8169_close() we can remove the call to cancel_work() +because we now call disable_work_sync() in rtl8169_down() already. + +Signed-off-by: Heiner Kallweit +Signed-off-by: Andrew Lunn +--- + drivers/net/ethernet/realtek/r8169_main.c | 18 ++++++------------ + 1 file changed, 6 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -619,7 +619,6 @@ struct rtl8169_tc_offsets { + }; + + enum rtl_flag { +- RTL_FLAG_TASK_ENABLED = 0, + RTL_FLAG_TASK_RESET_PENDING, + RTL_FLAG_TASK_RESET_NO_QUEUE_WAKE, + RTL_FLAG_TASK_TX_TIMEOUT, +@@ -2505,11 +2504,9 @@ u16 rtl8168h_2_get_adc_bias_ioffset(stru + + static void rtl_schedule_task(struct rtl8169_private *tp, enum rtl_flag flag) + { +- if (!test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags)) +- return; +- + set_bit(flag, tp->wk.flags); +- schedule_work(&tp->wk.work); ++ if (!schedule_work(&tp->wk.work)) ++ clear_bit(flag, tp->wk.flags); + } + + static void rtl8169_init_phy(struct rtl8169_private *tp) +@@ -4833,9 +4830,6 @@ static void rtl_task(struct work_struct + container_of(work, struct rtl8169_private, wk.work); + int ret; + +- if (!test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags)) +- return; +- + if (test_and_clear_bit(RTL_FLAG_TASK_TX_TIMEOUT, tp->wk.flags)) { + /* if chip isn't accessible, reset bus to revive it */ + if (RTL_R32(tp, TxConfig) == ~0) { +@@ -4919,6 +4913,7 @@ static int r8169_phy_connect(struct rtl8 + + static void rtl8169_down(struct rtl8169_private *tp) + { ++ disable_work_sync(&tp->wk.work); + /* Clear all task flags */ + bitmap_zero(tp->wk.flags, RTL_FLAG_MAX); + +@@ -4947,7 +4942,7 @@ static void rtl8169_up(struct rtl8169_pr + phy_resume(tp->phydev); + rtl8169_init_phy(tp); + napi_enable(&tp->napi); +- set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags); ++ enable_work(&tp->wk.work); + rtl_reset_work(tp); + + phy_start(tp->phydev); +@@ -4964,8 +4959,6 @@ static int rtl8169_close(struct net_devi + rtl8169_down(tp); + rtl8169_rx_clear(tp); + +- cancel_work(&tp->wk.work); +- + free_irq(tp->irq, tp); + + phy_disconnect(tp->phydev); +@@ -5199,7 +5192,7 @@ static void rtl_remove_one(struct pci_de + if (pci_dev_run_wake(pdev)) + pm_runtime_get_noresume(&pdev->dev); + +- cancel_work_sync(&tp->wk.work); ++ disable_work_sync(&tp->wk.work); + + if (IS_ENABLED(CONFIG_R8169_LEDS)) + r8169_remove_leds(tp->leds); +@@ -5577,6 +5570,7 @@ static int rtl_init_one(struct pci_dev * + tp->irq = pci_irq_vector(pdev, 0); + + INIT_WORK(&tp->wk.work, rtl_task); ++ disable_work(&tp->wk.work); + + rtl_init_mac_address(tp); + diff --git a/target/linux/generic/backport-6.12/780-06-v6.13-r8169-avoid-duplicated-messages-if-loading-firmware-.patch b/target/linux/generic/backport-6.12/780-06-v6.13-r8169-avoid-duplicated-messages-if-loading-firmware-.patch new file mode 100644 index 00000000000..6430d32252b --- /dev/null +++ b/target/linux/generic/backport-6.12/780-06-v6.13-r8169-avoid-duplicated-messages-if-loading-firmware-.patch @@ -0,0 +1,41 @@ +From 1c105bacb160b5918e917ab811552b7be69fc69c Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 16 Oct 2024 22:29:39 +0200 +Subject: [PATCH] r8169: avoid duplicated messages if loading firmware fails + and switch to warn level + +In case of a problem with firmware loading we inform at the driver level, +in addition the firmware load code itself issues warnings. Therefore +switch to firmware_request_nowarn() to avoid duplicated error messages. +In addition switch to warn level because the firmware is optional and +typically just fixes compatibility issues. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Message-ID: +Signed-off-by: Andrew Lunn +--- + drivers/net/ethernet/realtek/r8169_firmware.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_firmware.c ++++ b/drivers/net/ethernet/realtek/r8169_firmware.c +@@ -215,7 +215,7 @@ int rtl_fw_request_firmware(struct rtl_f + { + int rc; + +- rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev); ++ rc = firmware_request_nowarn(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev); + if (rc < 0) + goto out; + +@@ -227,7 +227,7 @@ int rtl_fw_request_firmware(struct rtl_f + + return 0; + out: +- dev_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n", +- rtl_fw->fw_name, rc); ++ dev_warn(rtl_fw->dev, "Unable to load firmware %s (%d)\n", ++ rtl_fw->fw_name, rc); + return rc; + } diff --git a/target/linux/generic/backport-6.12/780-07-v6.13-r8169-remove-rtl_dash_loop_wait_high-low.patch b/target/linux/generic/backport-6.12/780-07-v6.13-r8169-remove-rtl_dash_loop_wait_high-low.patch new file mode 100644 index 00000000000..bc8f979c20c --- /dev/null +++ b/target/linux/generic/backport-6.12/780-07-v6.13-r8169-remove-rtl_dash_loop_wait_high-low.patch @@ -0,0 +1,82 @@ +From d64113c6bb5ea5a70b7c9c3a6bcadef307638187 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 16 Oct 2024 22:31:10 +0200 +Subject: [PATCH] r8169: remove rtl_dash_loop_wait_high/low + +Remove rtl_dash_loop_wait_high/low to simplify the code. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Message-ID: +Signed-off-by: Andrew Lunn +--- + drivers/net/ethernet/realtek/r8169_main.c | 35 ++++++----------------- + 1 file changed, 8 insertions(+), 27 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -1348,40 +1348,19 @@ static void rtl8168ep_stop_cmac(struct r + RTL_W8(tp, IBCR0, RTL_R8(tp, IBCR0) & ~0x01); + } + +-static void rtl_dash_loop_wait(struct rtl8169_private *tp, +- const struct rtl_cond *c, +- unsigned long usecs, int n, bool high) +-{ +- if (!tp->dash_enabled) +- return; +- rtl_loop_wait(tp, c, usecs, n, high); +-} +- +-static void rtl_dash_loop_wait_high(struct rtl8169_private *tp, +- const struct rtl_cond *c, +- unsigned long d, int n) +-{ +- rtl_dash_loop_wait(tp, c, d, n, true); +-} +- +-static void rtl_dash_loop_wait_low(struct rtl8169_private *tp, +- const struct rtl_cond *c, +- unsigned long d, int n) +-{ +- rtl_dash_loop_wait(tp, c, d, n, false); +-} +- + static void rtl8168dp_driver_start(struct rtl8169_private *tp) + { + r8168dp_oob_notify(tp, OOB_CMD_DRIVER_START); +- rtl_dash_loop_wait_high(tp, &rtl_dp_ocp_read_cond, 10000, 10); ++ if (tp->dash_enabled) ++ rtl_loop_wait_high(tp, &rtl_dp_ocp_read_cond, 10000, 10); + } + + static void rtl8168ep_driver_start(struct rtl8169_private *tp) + { + r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START); + r8168ep_ocp_write(tp, 0x01, 0x30, r8168ep_ocp_read(tp, 0x30) | 0x01); +- rtl_dash_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 30); ++ if (tp->dash_enabled) ++ rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 30); + } + + static void rtl8168_driver_start(struct rtl8169_private *tp) +@@ -1395,7 +1374,8 @@ static void rtl8168_driver_start(struct + static void rtl8168dp_driver_stop(struct rtl8169_private *tp) + { + r8168dp_oob_notify(tp, OOB_CMD_DRIVER_STOP); +- rtl_dash_loop_wait_low(tp, &rtl_dp_ocp_read_cond, 10000, 10); ++ if (tp->dash_enabled) ++ rtl_loop_wait_low(tp, &rtl_dp_ocp_read_cond, 10000, 10); + } + + static void rtl8168ep_driver_stop(struct rtl8169_private *tp) +@@ -1403,7 +1383,8 @@ static void rtl8168ep_driver_stop(struct + rtl8168ep_stop_cmac(tp); + r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_STOP); + r8168ep_ocp_write(tp, 0x01, 0x30, r8168ep_ocp_read(tp, 0x30) | 0x01); +- rtl_dash_loop_wait_low(tp, &rtl_ep_ocp_read_cond, 10000, 10); ++ if (tp->dash_enabled) ++ rtl_loop_wait_low(tp, &rtl_ep_ocp_read_cond, 10000, 10); + } + + static void rtl8168_driver_stop(struct rtl8169_private *tp) diff --git a/target/linux/generic/backport-6.12/780-08-v6.13-r8169-enable-EEE-at-2.5G-per-default-on-RTL8125B.patch b/target/linux/generic/backport-6.12/780-08-v6.13-r8169-enable-EEE-at-2.5G-per-default-on-RTL8125B.patch new file mode 100644 index 00000000000..4ec06cc6019 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-08-v6.13-r8169-enable-EEE-at-2.5G-per-default-on-RTL8125B.patch @@ -0,0 +1,28 @@ +From c4e64095c00cb2de413cd6b90be047c273bcd491 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Thu, 17 Oct 2024 22:27:44 +0200 +Subject: [PATCH] r8169: enable EEE at 2.5G per default on RTL8125B + +Register a6d/12 is shadowing register MDIO_AN_EEE_ADV2. So this line +disables advertisement of EEE at 2.5G. Latest vendor driver r8125 +doesn't do this (any longer?), so this mode seems to be safe. +EEE saves quite some energy, therefore enable this mode per default. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Message-ID: <95dd5a0c-09ea-4847-94d9-b7aa3063e8ff@gmail.com> +Signed-off-by: Andrew Lunn +--- + drivers/net/ethernet/realtek/r8169_phy_config.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -99,7 +99,6 @@ static void rtl8125a_config_eee_phy(stru + + static void rtl8125b_config_eee_phy(struct phy_device *phydev) + { +- phy_modify_paged(phydev, 0xa6d, 0x12, 0x0001, 0x0000); + phy_modify_paged(phydev, 0xa6d, 0x14, 0x0010, 0x0000); + phy_modify_paged(phydev, 0xa42, 0x14, 0x0080, 0x0000); + phy_modify_paged(phydev, 0xa4a, 0x11, 0x0200, 0x0000); diff --git a/target/linux/generic/backport-6.12/780-10-v6.13-r8169-fix-inconsistent-indenting-in-rtl8169_get_eth_.patch b/target/linux/generic/backport-6.12/780-10-v6.13-r8169-fix-inconsistent-indenting-in-rtl8169_get_eth_.patch new file mode 100644 index 00000000000..41bab553664 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-10-v6.13-r8169-fix-inconsistent-indenting-in-rtl8169_get_eth_.patch @@ -0,0 +1,30 @@ +From b8bd8c44a266c9a7dcb907eab10fbb119e3f6494 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Thu, 24 Oct 2024 22:48:59 +0200 +Subject: [PATCH] r8169: fix inconsistent indenting in + rtl8169_get_eth_mac_stats + +This fixes an inconsistent indenting introduced with e3fc5139bd8f +("r8169: implement additional ethtool stats ops"). + +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202410220413.1gAxIJ4t-lkp@intel.com/ +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20fd6f39-3c1b-4af0-9adc-7d1f49728fad@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -2224,7 +2224,7 @@ static void rtl8169_get_eth_mac_stats(st + le64_to_cpu(tp->counters->tx_broadcast64); + mac_stats->MulticastFramesReceivedOK = + le64_to_cpu(tp->counters->rx_multicast64); +- mac_stats->FrameTooLongErrors = ++ mac_stats->FrameTooLongErrors = + le32_to_cpu(tp->counters->rx_frame_too_long); + } + diff --git a/target/linux/generic/backport-6.12/780-11-v6.13-r8169-align-RTL8125-EEE-config-with-vendor-driver.patch b/target/linux/generic/backport-6.12/780-11-v6.13-r8169-align-RTL8125-EEE-config-with-vendor-driver.patch new file mode 100644 index 00000000000..de2be0165c2 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-11-v6.13-r8169-align-RTL8125-EEE-config-with-vendor-driver.patch @@ -0,0 +1,49 @@ +From eb90f876b7961d702d7fc549e14614860f531e60 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Thu, 31 Oct 2024 22:42:52 +0100 +Subject: [PATCH] r8169: align RTL8125 EEE config with vendor driver + +Align the EEE config for RTL8125A/RTL8125B with vendor driver r8125. +This should help to avoid compatibility issues. + +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/044c925e-8669-4b98-87df-95b4056f4f5f@gmail.com +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/realtek/r8169_phy_config.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -89,19 +89,25 @@ static void rtl8168h_config_eee_phy(stru + phy_modify_paged(phydev, 0xa42, 0x14, 0x0000, 0x0080); + } + +-static void rtl8125a_config_eee_phy(struct phy_device *phydev) ++static void rtl8125_common_config_eee_phy(struct phy_device *phydev) + { +- rtl8168h_config_eee_phy(phydev); ++ phy_modify_paged(phydev, 0xa6d, 0x14, 0x0010, 0x0000); ++ phy_modify_paged(phydev, 0xa42, 0x14, 0x0080, 0x0000); ++ phy_modify_paged(phydev, 0xa4a, 0x11, 0x0200, 0x0000); ++} + ++static void rtl8125a_config_eee_phy(struct phy_device *phydev) ++{ ++ rtl8168g_config_eee_phy(phydev); ++ /* disable EEE at 2.5Gbps */ + phy_modify_paged(phydev, 0xa6d, 0x12, 0x0001, 0x0000); +- phy_modify_paged(phydev, 0xa6d, 0x14, 0x0010, 0x0000); ++ rtl8125_common_config_eee_phy(phydev); + } + + static void rtl8125b_config_eee_phy(struct phy_device *phydev) + { +- phy_modify_paged(phydev, 0xa6d, 0x14, 0x0010, 0x0000); +- phy_modify_paged(phydev, 0xa42, 0x14, 0x0080, 0x0000); +- phy_modify_paged(phydev, 0xa4a, 0x11, 0x0200, 0x0000); ++ rtl8168g_config_eee_phy(phydev); ++ rtl8125_common_config_eee_phy(phydev); + } + + static void rtl8169s_hw_phy_config(struct rtl8169_private *tp, diff --git a/target/linux/generic/backport-6.12/780-12-v6.13-r8169-align-RTL8125-RTL8126-PHY-config-with-vendor-d.patch b/target/linux/generic/backport-6.12/780-12-v6.13-r8169-align-RTL8125-RTL8126-PHY-config-with-vendor-d.patch new file mode 100644 index 00000000000..a546c426b49 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-12-v6.13-r8169-align-RTL8125-RTL8126-PHY-config-with-vendor-d.patch @@ -0,0 +1,46 @@ +From 4af2f60bf7378bd5c92b15a528d8c6c7d02bed6c Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Thu, 31 Oct 2024 22:43:45 +0100 +Subject: [PATCH] r8169: align RTL8125/RTL8126 PHY config with vendor driver + +This aligns some parameters with vendor driver r8125/r8126 to avoid +compatibility issues. Note that for RTL8125B there's no functional +change, just the open-coded version of the function is replaced. + +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/a8a9d896-fbe6-41f2-bf87-666567d3cdb3@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_phy_config.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -1073,8 +1073,8 @@ static void rtl8125b_hw_phy_config(struc + struct phy_device *phydev) + { + r8169_apply_firmware(tp); ++ rtl8168g_enable_gphy_10m(phydev); + +- phy_modify_paged(phydev, 0xa44, 0x11, 0x0000, 0x0800); + phy_modify_paged(phydev, 0xac4, 0x13, 0x00f0, 0x0090); + phy_modify_paged(phydev, 0xad3, 0x10, 0x0003, 0x0001); + +@@ -1113,6 +1113,7 @@ static void rtl8125d_hw_phy_config(struc + struct phy_device *phydev) + { + r8169_apply_firmware(tp); ++ rtl8168g_enable_gphy_10m(phydev); + rtl8125_legacy_force_mode(phydev); + rtl8168g_disable_aldps(phydev); + rtl8125b_config_eee_phy(phydev); +@@ -1122,6 +1123,9 @@ static void rtl8126a_hw_phy_config(struc + struct phy_device *phydev) + { + r8169_apply_firmware(tp); ++ rtl8168g_enable_gphy_10m(phydev); ++ rtl8125_legacy_force_mode(phydev); ++ rtl8168g_disable_aldps(phydev); + } + + void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, diff --git a/target/linux/generic/backport-6.12/780-13-v6.13-r8169-align-RTL8126-EEE-config-with-vendor-driver.patch b/target/linux/generic/backport-6.12/780-13-v6.13-r8169-align-RTL8126-EEE-config-with-vendor-driver.patch new file mode 100644 index 00000000000..36c8041c94e --- /dev/null +++ b/target/linux/generic/backport-6.12/780-13-v6.13-r8169-align-RTL8126-EEE-config-with-vendor-driver.patch @@ -0,0 +1,25 @@ +From a3d8520e6a19ab018da6c7fc22512c913697a829 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Thu, 31 Oct 2024 22:44:36 +0100 +Subject: [PATCH] r8169: align RTL8126 EEE config with vendor driver + +Align the EEE config for RTL8126A with vendor driver r8126 to avoid +compatibility issues. + +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/71e4859e-4cd0-4b6b-b7fa-621d7721992f@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_phy_config.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -1126,6 +1126,7 @@ static void rtl8126a_hw_phy_config(struc + rtl8168g_enable_gphy_10m(phydev); + rtl8125_legacy_force_mode(phydev); + rtl8168g_disable_aldps(phydev); ++ rtl8125_common_config_eee_phy(phydev); + } + + void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, diff --git a/target/linux/generic/backport-6.12/780-14-v6.13-r8169-improve-initialization-of-RSS-registers-on-RTL.patch b/target/linux/generic/backport-6.12/780-14-v6.13-r8169-improve-initialization-of-RSS-registers-on-RTL.patch new file mode 100644 index 00000000000..d00b08e0fae --- /dev/null +++ b/target/linux/generic/backport-6.12/780-14-v6.13-r8169-improve-initialization-of-RSS-registers-on-RTL.patch @@ -0,0 +1,38 @@ +From 2cd02f2fdd8a92e5b6b85ff64eab0fc549b30c07 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Sat, 2 Nov 2024 14:49:01 +0100 +Subject: [PATCH] r8169: improve initialization of RSS registers on + RTL8125/RTL8126 + +Replace the register addresses with the names used in r8125/r8126 +vendor driver, and consider that RSS_CTRL_8125 is a 32 bit register. + +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/3bf2f340-b369-4174-97bf-fd38d4217492@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -346,6 +346,8 @@ enum rtl8125_registers { + TxPoll_8125 = 0x90, + LEDSEL3 = 0x96, + MAC0_BKP = 0x19e0, ++ RSS_CTRL_8125 = 0x4500, ++ Q_NUM_CTRL_8125 = 0x4800, + EEE_TXIDLE_TIMER_8125 = 0x6048, + }; + +@@ -3788,8 +3790,8 @@ static void rtl_hw_start_8125_common(str + rtl_pcie_state_l2l3_disable(tp); + + RTL_W16(tp, 0x382, 0x221b); +- RTL_W8(tp, 0x4500, 0); +- RTL_W16(tp, 0x4800, 0); ++ RTL_W32(tp, RSS_CTRL_8125, 0); ++ RTL_W16(tp, Q_NUM_CTRL_8125, 0); + + /* disable UPS */ + r8168_mac_ocp_modify(tp, 0xd40a, 0x0010, 0x0000); diff --git a/target/linux/generic/backport-6.12/780-15-v6.13-r8169-remove-leftover-locks-after-reverted-change.patch b/target/linux/generic/backport-6.12/780-15-v6.13-r8169-remove-leftover-locks-after-reverted-change.patch new file mode 100644 index 00000000000..19bcf08d55b --- /dev/null +++ b/target/linux/generic/backport-6.12/780-15-v6.13-r8169-remove-leftover-locks-after-reverted-change.patch @@ -0,0 +1,113 @@ +From 83cb4b470c66b37b19a347a35cea01e0cbdd258d Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Mon, 4 Nov 2024 23:16:20 +0100 +Subject: [PATCH] r8169: remove leftover locks after reverted change + +After e31a9fedc7d8 ("Revert "r8169: disable ASPM during NAPI poll"") +these locks aren't needed any longer. + +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/680f2606-ac7d-4ced-8694-e5033855da9b@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 29 ++--------------------- + 1 file changed, 2 insertions(+), 27 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -661,13 +661,9 @@ struct rtl8169_private { + struct work_struct work; + } wk; + +- raw_spinlock_t config25_lock; + raw_spinlock_t mac_ocp_lock; + struct mutex led_lock; /* serialize LED ctrl RMW access */ + +- raw_spinlock_t cfg9346_usage_lock; +- int cfg9346_usage_count; +- + unsigned supports_gmii:1; + unsigned aspm_manageable:1; + unsigned dash_enabled:1; +@@ -721,22 +717,12 @@ static inline struct device *tp_to_dev(s + + static void rtl_lock_config_regs(struct rtl8169_private *tp) + { +- unsigned long flags; +- +- raw_spin_lock_irqsave(&tp->cfg9346_usage_lock, flags); +- if (!--tp->cfg9346_usage_count) +- RTL_W8(tp, Cfg9346, Cfg9346_Lock); +- raw_spin_unlock_irqrestore(&tp->cfg9346_usage_lock, flags); ++ RTL_W8(tp, Cfg9346, Cfg9346_Lock); + } + + static void rtl_unlock_config_regs(struct rtl8169_private *tp) + { +- unsigned long flags; +- +- raw_spin_lock_irqsave(&tp->cfg9346_usage_lock, flags); +- if (!tp->cfg9346_usage_count++) +- RTL_W8(tp, Cfg9346, Cfg9346_Unlock); +- raw_spin_unlock_irqrestore(&tp->cfg9346_usage_lock, flags); ++ RTL_W8(tp, Cfg9346, Cfg9346_Unlock); + } + + static void rtl_pci_commit(struct rtl8169_private *tp) +@@ -747,24 +733,18 @@ static void rtl_pci_commit(struct rtl816 + + static void rtl_mod_config2(struct rtl8169_private *tp, u8 clear, u8 set) + { +- unsigned long flags; + u8 val; + +- raw_spin_lock_irqsave(&tp->config25_lock, flags); + val = RTL_R8(tp, Config2); + RTL_W8(tp, Config2, (val & ~clear) | set); +- raw_spin_unlock_irqrestore(&tp->config25_lock, flags); + } + + static void rtl_mod_config5(struct rtl8169_private *tp, u8 clear, u8 set) + { +- unsigned long flags; + u8 val; + +- raw_spin_lock_irqsave(&tp->config25_lock, flags); + val = RTL_R8(tp, Config5); + RTL_W8(tp, Config5, (val & ~clear) | set); +- raw_spin_unlock_irqrestore(&tp->config25_lock, flags); + } + + static bool rtl_is_8125(struct rtl8169_private *tp) +@@ -1570,7 +1550,6 @@ static void __rtl8169_set_wol(struct rtl + { WAKE_MAGIC, Config3, MagicPacket } + }; + unsigned int i, tmp = ARRAY_SIZE(cfg); +- unsigned long flags; + u8 options; + + rtl_unlock_config_regs(tp); +@@ -1589,14 +1568,12 @@ static void __rtl8169_set_wol(struct rtl + r8168_mac_ocp_modify(tp, 0xc0b6, BIT(0), 0); + } + +- raw_spin_lock_irqsave(&tp->config25_lock, flags); + for (i = 0; i < tmp; i++) { + options = RTL_R8(tp, cfg[i].reg) & ~cfg[i].mask; + if (wolopts & cfg[i].opt) + options |= cfg[i].mask; + RTL_W8(tp, cfg[i].reg, options); + } +- raw_spin_unlock_irqrestore(&tp->config25_lock, flags); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: +@@ -5477,8 +5454,6 @@ static int rtl_init_one(struct pci_dev * + tp->supports_gmii = ent->driver_data == RTL_CFG_NO_GBIT ? 0 : 1; + tp->ocp_base = OCP_STD_PHY_BASE; + +- raw_spin_lock_init(&tp->cfg9346_usage_lock); +- raw_spin_lock_init(&tp->config25_lock); + raw_spin_lock_init(&tp->mac_ocp_lock); + mutex_init(&tp->led_lock); + diff --git a/target/linux/generic/backport-6.12/780-16-v6.13-r8169-improve-__rtl8169_set_wol.patch b/target/linux/generic/backport-6.12/780-16-v6.13-r8169-improve-__rtl8169_set_wol.patch new file mode 100644 index 00000000000..3158ac51711 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-16-v6.13-r8169-improve-__rtl8169_set_wol.patch @@ -0,0 +1,108 @@ +From c507e96b5763b36b63ad50ad804341f72ea000e4 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 6 Nov 2024 17:55:45 +0100 +Subject: [PATCH] r8169: improve __rtl8169_set_wol + +Add helper r8169_mod_reg8_cond() what allows to significantly simplify +__rtl8169_set_wol(). + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/697b197a-8eac-40c6-8847-27093cacec36@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 55 ++++++++++------------- + 1 file changed, 24 insertions(+), 31 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -747,6 +747,20 @@ static void rtl_mod_config5(struct rtl81 + RTL_W8(tp, Config5, (val & ~clear) | set); + } + ++static void r8169_mod_reg8_cond(struct rtl8169_private *tp, int reg, ++ u8 bits, bool cond) ++{ ++ u8 val, old_val; ++ ++ old_val = RTL_R8(tp, reg); ++ if (cond) ++ val = old_val | bits; ++ else ++ val = old_val & ~bits; ++ if (val != old_val) ++ RTL_W8(tp, reg, val); ++} ++ + static bool rtl_is_8125(struct rtl8169_private *tp) + { + return tp->mac_version >= RTL_GIGA_MAC_VER_61; +@@ -1537,58 +1551,37 @@ static void rtl8169_get_wol(struct net_d + + static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) + { +- static const struct { +- u32 opt; +- u16 reg; +- u8 mask; +- } cfg[] = { +- { WAKE_PHY, Config3, LinkUp }, +- { WAKE_UCAST, Config5, UWF }, +- { WAKE_BCAST, Config5, BWF }, +- { WAKE_MCAST, Config5, MWF }, +- { WAKE_ANY, Config5, LanWake }, +- { WAKE_MAGIC, Config3, MagicPacket } +- }; +- unsigned int i, tmp = ARRAY_SIZE(cfg); +- u8 options; +- + rtl_unlock_config_regs(tp); + + if (rtl_is_8168evl_up(tp)) { +- tmp--; + if (wolopts & WAKE_MAGIC) + rtl_eri_set_bits(tp, 0x0dc, MagicPacket_v2); + else + rtl_eri_clear_bits(tp, 0x0dc, MagicPacket_v2); + } else if (rtl_is_8125(tp)) { +- tmp--; + if (wolopts & WAKE_MAGIC) + r8168_mac_ocp_modify(tp, 0xc0b6, 0, BIT(0)); + else + r8168_mac_ocp_modify(tp, 0xc0b6, BIT(0), 0); ++ } else { ++ r8169_mod_reg8_cond(tp, Config3, MagicPacket, ++ wolopts & WAKE_MAGIC); + } + +- for (i = 0; i < tmp; i++) { +- options = RTL_R8(tp, cfg[i].reg) & ~cfg[i].mask; +- if (wolopts & cfg[i].opt) +- options |= cfg[i].mask; +- RTL_W8(tp, cfg[i].reg, options); +- } ++ r8169_mod_reg8_cond(tp, Config3, LinkUp, wolopts & WAKE_PHY); ++ r8169_mod_reg8_cond(tp, Config5, UWF, wolopts & WAKE_UCAST); ++ r8169_mod_reg8_cond(tp, Config5, BWF, wolopts & WAKE_BCAST); ++ r8169_mod_reg8_cond(tp, Config5, MWF, wolopts & WAKE_MCAST); ++ r8169_mod_reg8_cond(tp, Config5, LanWake, wolopts); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: +- options = RTL_R8(tp, Config1) & ~PMEnable; +- if (wolopts) +- options |= PMEnable; +- RTL_W8(tp, Config1, options); ++ r8169_mod_reg8_cond(tp, Config1, PMEnable, wolopts); + break; + case RTL_GIGA_MAC_VER_34: + case RTL_GIGA_MAC_VER_37: + case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_66: +- if (wolopts) +- rtl_mod_config2(tp, 0, PME_SIGNAL); +- else +- rtl_mod_config2(tp, PME_SIGNAL, 0); ++ r8169_mod_reg8_cond(tp, Config2, PME_SIGNAL, wolopts); + break; + default: + break; diff --git a/target/linux/generic/backport-6.12/780-17-v6.13-r8169-improve-rtl_set_d3_pll_down.patch b/target/linux/generic/backport-6.12/780-17-v6.13-r8169-improve-rtl_set_d3_pll_down.patch new file mode 100644 index 00000000000..ede589ee734 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-17-v6.13-r8169-improve-rtl_set_d3_pll_down.patch @@ -0,0 +1,44 @@ +From 330dc2297c82953dff402e0b4176a5383a618538 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 6 Nov 2024 17:56:28 +0100 +Subject: [PATCH] r8169: improve rtl_set_d3_pll_down + +Make use of new helper r8169_mod_reg8_cond() and move from a switch() +to an if() clause. Benefit is that we don't have to touch this piece of +code each time support for a new chip version is added. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/e1ccdb85-a4ed-4800-89c2-89770ff06452@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 18 +++++------------- + 1 file changed, 5 insertions(+), 13 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -1430,19 +1430,11 @@ static enum rtl_dash_type rtl_get_dash_t + + static void rtl_set_d3_pll_down(struct rtl8169_private *tp, bool enable) + { +- switch (tp->mac_version) { +- case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26: +- case RTL_GIGA_MAC_VER_29 ... RTL_GIGA_MAC_VER_30: +- case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37: +- case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_66: +- if (enable) +- RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN); +- else +- RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | D3_NO_PLL_DOWN); +- break; +- default: +- break; +- } ++ if (tp->mac_version >= RTL_GIGA_MAC_VER_25 && ++ tp->mac_version != RTL_GIGA_MAC_VER_28 && ++ tp->mac_version != RTL_GIGA_MAC_VER_31 && ++ tp->mac_version != RTL_GIGA_MAC_VER_38) ++ r8169_mod_reg8_cond(tp, PMCH, D3_NO_PLL_DOWN, !enable); + } + + static void rtl_reset_packet_filter(struct rtl8169_private *tp) diff --git a/target/linux/generic/backport-6.12/780-18-v6.13-r8169-align-WAKE_PHY-handling-with-r8125-r8126-vendo.patch b/target/linux/generic/backport-6.12/780-18-v6.13-r8169-align-WAKE_PHY-handling-with-r8125-r8126-vendo.patch new file mode 100644 index 00000000000..ceb8c1239f0 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-18-v6.13-r8169-align-WAKE_PHY-handling-with-r8125-r8126-vendo.patch @@ -0,0 +1,29 @@ +From e3e9e9039fa6ae885c7d5c954d7b9f105fa23e8f Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 6 Nov 2024 17:57:08 +0100 +Subject: [PATCH] r8169: align WAKE_PHY handling with r8125/r8126 vendor + drivers + +Vendor drivers r8125/r8126 apply this additional magic setting when +enabling WAKE_PHY, so do the same here. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/51130715-45be-4db5-abb7-05d87e1f5df9@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -1561,6 +1561,9 @@ static void __rtl8169_set_wol(struct rtl + } + + r8169_mod_reg8_cond(tp, Config3, LinkUp, wolopts & WAKE_PHY); ++ if (rtl_is_8125(tp)) ++ r8168_mac_ocp_modify(tp, 0xe0c6, 0x3f, ++ wolopts & WAKE_PHY ? 0x13 : 0); + r8169_mod_reg8_cond(tp, Config5, UWF, wolopts & WAKE_UCAST); + r8169_mod_reg8_cond(tp, Config5, BWF, wolopts & WAKE_BCAST); + r8169_mod_reg8_cond(tp, Config5, MWF, wolopts & WAKE_MCAST); diff --git a/target/linux/generic/backport-6.12/780-19-v6.13-r8169-use-helper-r8169_mod_reg8_cond-to-simplify-rtl.patch b/target/linux/generic/backport-6.12/780-19-v6.13-r8169-use-helper-r8169_mod_reg8_cond-to-simplify-rtl.patch new file mode 100644 index 00000000000..d6332dfa140 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-19-v6.13-r8169-use-helper-r8169_mod_reg8_cond-to-simplify-rtl.patch @@ -0,0 +1,117 @@ +From 7a3bcd39ae1f0e3ab896d9df62339ab4297a0bfd Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Sat, 9 Nov 2024 23:12:12 +0100 +Subject: [PATCH] r8169: use helper r8169_mod_reg8_cond to simplify + rtl_jumbo_config + +Use recently added helper r8169_mod_reg8_cond() to simplify jumbo +mode configuration. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/3df1d484-a02e-46e7-8f75-db5b428e422e@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 77 ++++------------------- + 1 file changed, 11 insertions(+), 66 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -2542,86 +2542,31 @@ static void rtl8169_init_ring_indexes(st + tp->dirty_tx = tp->cur_tx = tp->cur_rx = 0; + } + +-static void r8168c_hw_jumbo_enable(struct rtl8169_private *tp) +-{ +- RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); +- RTL_W8(tp, Config4, RTL_R8(tp, Config4) | Jumbo_En1); +-} +- +-static void r8168c_hw_jumbo_disable(struct rtl8169_private *tp) +-{ +- RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); +- RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~Jumbo_En1); +-} +- +-static void r8168dp_hw_jumbo_enable(struct rtl8169_private *tp) +-{ +- RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); +-} +- +-static void r8168dp_hw_jumbo_disable(struct rtl8169_private *tp) +-{ +- RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); +-} +- +-static void r8168e_hw_jumbo_enable(struct rtl8169_private *tp) +-{ +- RTL_W8(tp, MaxTxPacketSize, 0x24); +- RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); +- RTL_W8(tp, Config4, RTL_R8(tp, Config4) | 0x01); +-} +- +-static void r8168e_hw_jumbo_disable(struct rtl8169_private *tp) +-{ +- RTL_W8(tp, MaxTxPacketSize, 0x3f); +- RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); +- RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~0x01); +-} +- +-static void r8168b_1_hw_jumbo_enable(struct rtl8169_private *tp) +-{ +- RTL_W8(tp, Config4, RTL_R8(tp, Config4) | (1 << 0)); +-} +- +-static void r8168b_1_hw_jumbo_disable(struct rtl8169_private *tp) +-{ +- RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~(1 << 0)); +-} +- + static void rtl_jumbo_config(struct rtl8169_private *tp) + { + bool jumbo = tp->dev->mtu > ETH_DATA_LEN; + int readrq = 4096; + ++ if (jumbo && tp->mac_version >= RTL_GIGA_MAC_VER_17 && ++ tp->mac_version <= RTL_GIGA_MAC_VER_26) ++ readrq = 512; ++ + rtl_unlock_config_regs(tp); + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_17: +- if (jumbo) { +- readrq = 512; +- r8168b_1_hw_jumbo_enable(tp); +- } else { +- r8168b_1_hw_jumbo_disable(tp); +- } ++ r8169_mod_reg8_cond(tp, Config4, BIT(0), jumbo); + break; + case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_26: +- if (jumbo) { +- readrq = 512; +- r8168c_hw_jumbo_enable(tp); +- } else { +- r8168c_hw_jumbo_disable(tp); +- } ++ r8169_mod_reg8_cond(tp, Config3, Jumbo_En0, jumbo); ++ r8169_mod_reg8_cond(tp, Config4, Jumbo_En1, jumbo); + break; + case RTL_GIGA_MAC_VER_28: +- if (jumbo) +- r8168dp_hw_jumbo_enable(tp); +- else +- r8168dp_hw_jumbo_disable(tp); ++ r8169_mod_reg8_cond(tp, Config3, Jumbo_En0, jumbo); + break; + case RTL_GIGA_MAC_VER_31 ... RTL_GIGA_MAC_VER_33: +- if (jumbo) +- r8168e_hw_jumbo_enable(tp); +- else +- r8168e_hw_jumbo_disable(tp); ++ RTL_W8(tp, MaxTxPacketSize, jumbo ? 0x24 : 0x3f); ++ r8169_mod_reg8_cond(tp, Config3, Jumbo_En0, jumbo); ++ r8169_mod_reg8_cond(tp, Config4, BIT(0), jumbo); + break; + default: + break; diff --git a/target/linux/generic/backport-6.12/780-20-v6.13-r8169-copy-vendor-driver-2.5G-5G-EEE-advertisement-c.patch b/target/linux/generic/backport-6.12/780-20-v6.13-r8169-copy-vendor-driver-2.5G-5G-EEE-advertisement-c.patch new file mode 100644 index 00000000000..fee9d953685 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-20-v6.13-r8169-copy-vendor-driver-2.5G-5G-EEE-advertisement-c.patch @@ -0,0 +1,82 @@ +From e340bff27e63ed61a1e9895bed546107859e48a7 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Fri, 8 Nov 2024 08:08:24 +0100 +Subject: [PATCH] r8169: copy vendor driver 2.5G/5G EEE advertisement + constraints + +Vendor driver r8125 doesn't advertise 2.5G EEE on RTL8125A, and r8126 +doesn't advertise 5G EEE. Likely there are compatibility issues, +therefore do the same in r8169. +With this change we don't have to disable 2.5G EEE advertisement in +rtl8125a_config_eee_phy() any longer. +We use new phylib accessor phy_set_eee_broken() to mark the respective +EEE modes as broken. + +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/ce185e10-8a2f-4cf8-a49b-fd8fb3c3c8a1@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 6 ++++++ + drivers/net/ethernet/realtek/r8169_phy_config.c | 16 ++++------------ + 2 files changed, 10 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -5253,6 +5253,11 @@ static int r8169_mdio_register(struct rt + phy_support_eee(tp->phydev); + phy_support_asym_pause(tp->phydev); + ++ /* mimic behavior of r8125/r8126 vendor drivers */ ++ if (tp->mac_version == RTL_GIGA_MAC_VER_61) ++ tp->phydev->eee_broken_modes |= MDIO_EEE_2_5GT; ++ tp->phydev->eee_broken_modes |= MDIO_EEE_5GT; ++ + /* PHY will be woken up in rtl_open() */ + phy_suspend(tp->phydev); + +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -96,15 +96,7 @@ static void rtl8125_common_config_eee_ph + phy_modify_paged(phydev, 0xa4a, 0x11, 0x0200, 0x0000); + } + +-static void rtl8125a_config_eee_phy(struct phy_device *phydev) +-{ +- rtl8168g_config_eee_phy(phydev); +- /* disable EEE at 2.5Gbps */ +- phy_modify_paged(phydev, 0xa6d, 0x12, 0x0001, 0x0000); +- rtl8125_common_config_eee_phy(phydev); +-} +- +-static void rtl8125b_config_eee_phy(struct phy_device *phydev) ++static void rtl8125_config_eee_phy(struct phy_device *phydev) + { + rtl8168g_config_eee_phy(phydev); + rtl8125_common_config_eee_phy(phydev); +@@ -1066,7 +1058,7 @@ static void rtl8125a_2_hw_phy_config(str + rtl8168g_enable_gphy_10m(phydev); + + rtl8168g_disable_aldps(phydev); +- rtl8125a_config_eee_phy(phydev); ++ rtl8125_config_eee_phy(phydev); + } + + static void rtl8125b_hw_phy_config(struct rtl8169_private *tp, +@@ -1106,7 +1098,7 @@ static void rtl8125b_hw_phy_config(struc + + rtl8125_legacy_force_mode(phydev); + rtl8168g_disable_aldps(phydev); +- rtl8125b_config_eee_phy(phydev); ++ rtl8125_config_eee_phy(phydev); + } + + static void rtl8125d_hw_phy_config(struct rtl8169_private *tp, +@@ -1116,7 +1108,7 @@ static void rtl8125d_hw_phy_config(struc + rtl8168g_enable_gphy_10m(phydev); + rtl8125_legacy_force_mode(phydev); + rtl8168g_disable_aldps(phydev); +- rtl8125b_config_eee_phy(phydev); ++ rtl8125_config_eee_phy(phydev); + } + + static void rtl8126a_hw_phy_config(struct rtl8169_private *tp, diff --git a/target/linux/generic/backport-6.12/780-21-v6.14-r8169-remove-unused-flag-RTL_FLAG_TASK_RESET_NO_QUEU.patch b/target/linux/generic/backport-6.12/780-21-v6.14-r8169-remove-unused-flag-RTL_FLAG_TASK_RESET_NO_QUEU.patch new file mode 100644 index 00000000000..182858ac029 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-21-v6.14-r8169-remove-unused-flag-RTL_FLAG_TASK_RESET_NO_QUEU.patch @@ -0,0 +1,35 @@ +From 2e20bf8cc05766dcd0357cdfcada49e1bc45512b Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Mon, 2 Dec 2024 21:14:35 +0100 +Subject: [PATCH] r8169: remove unused flag RTL_FLAG_TASK_RESET_NO_QUEUE_WAKE + +After 854d71c555dfc3 ("r8169: remove original workaround for RTL8125 +broken rx issue") this flag isn't used any longer. So remove it. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Michal Swiatkowski +Link: https://patch.msgid.link/d9dd214b-3027-4f60-b0e8-6f34a0c76582@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -622,7 +622,6 @@ struct rtl8169_tc_offsets { + + enum rtl_flag { + RTL_FLAG_TASK_RESET_PENDING, +- RTL_FLAG_TASK_RESET_NO_QUEUE_WAKE, + RTL_FLAG_TASK_TX_TIMEOUT, + RTL_FLAG_MAX + }; +@@ -4746,8 +4745,6 @@ static void rtl_task(struct work_struct + reset: + rtl_reset_work(tp); + netif_wake_queue(tp->dev); +- } else if (test_and_clear_bit(RTL_FLAG_TASK_RESET_NO_QUEUE_WAKE, tp->wk.flags)) { +- rtl_reset_work(tp); + } + } + diff --git a/target/linux/generic/backport-6.12/780-22-v6.14-r8169-remove-support-for-chip-version-11.patch b/target/linux/generic/backport-6.12/780-22-v6.14-r8169-remove-support-for-chip-version-11.patch new file mode 100644 index 00000000000..055e0f01552 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-22-v6.14-r8169-remove-support-for-chip-version-11.patch @@ -0,0 +1,114 @@ +From bb18265c3aba92b91a1355609769f3e967b65dee Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Mon, 2 Dec 2024 21:20:02 +0100 +Subject: [PATCH] r8169: remove support for chip version 11 + +This is a follow-up to 982300c115d2 ("r8169: remove detection of chip +version 11 (early RTL8168b)"). Nobody complained yet, so remove +support for this chip version. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/b689ab6d-20b5-4b64-bd7e-531a0a972ba3@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169.h | 2 +- + drivers/net/ethernet/realtek/r8169_main.c | 14 +------------- + drivers/net/ethernet/realtek/r8169_phy_config.c | 10 ---------- + 3 files changed, 2 insertions(+), 24 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169.h ++++ b/drivers/net/ethernet/realtek/r8169.h +@@ -23,7 +23,7 @@ enum mac_version { + RTL_GIGA_MAC_VER_08, + RTL_GIGA_MAC_VER_09, + RTL_GIGA_MAC_VER_10, +- RTL_GIGA_MAC_VER_11, ++ /* support for RTL_GIGA_MAC_VER_11 has been removed */ + /* RTL_GIGA_MAC_VER_12 was handled the same as VER_17 */ + /* RTL_GIGA_MAC_VER_13 was merged with VER_10 */ + RTL_GIGA_MAC_VER_14, +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -103,7 +103,6 @@ static const struct { + [RTL_GIGA_MAC_VER_08] = {"RTL8102e" }, + [RTL_GIGA_MAC_VER_09] = {"RTL8102e/RTL8103e" }, + [RTL_GIGA_MAC_VER_10] = {"RTL8101e/RTL8100e" }, +- [RTL_GIGA_MAC_VER_11] = {"RTL8168b/8111b" }, + [RTL_GIGA_MAC_VER_14] = {"RTL8401" }, + [RTL_GIGA_MAC_VER_17] = {"RTL8168b/8111b" }, + [RTL_GIGA_MAC_VER_18] = {"RTL8168cp/8111cp" }, +@@ -2334,7 +2333,7 @@ static enum mac_version rtl8169_get_mac_ + + /* 8168B family. */ + { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17 }, +- /* This one is very old and rare, let's see if anybody complains. ++ /* This one is very old and rare, support has been removed. + * { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 }, + */ + +@@ -3826,7 +3825,6 @@ static void rtl_hw_config(struct rtl8169 + [RTL_GIGA_MAC_VER_08] = rtl_hw_start_8102e_3, + [RTL_GIGA_MAC_VER_09] = rtl_hw_start_8102e_2, + [RTL_GIGA_MAC_VER_10] = NULL, +- [RTL_GIGA_MAC_VER_11] = rtl_hw_start_8168b, + [RTL_GIGA_MAC_VER_14] = rtl_hw_start_8401, + [RTL_GIGA_MAC_VER_17] = rtl_hw_start_8168b, + [RTL_GIGA_MAC_VER_18] = rtl_hw_start_8168cp_1, +@@ -4702,12 +4700,6 @@ static irqreturn_t rtl8169_interrupt(int + if (status & LinkChg) + phy_mac_interrupt(tp->phydev); + +- if (unlikely(status & RxFIFOOver && +- tp->mac_version == RTL_GIGA_MAC_VER_11)) { +- netif_stop_queue(tp->dev); +- rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING); +- } +- + rtl_irq_disable(tp); + napi_schedule(&tp->napi); + out: +@@ -5124,9 +5116,6 @@ static void rtl_set_irq_mask(struct rtl8 + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) + tp->irq_mask |= SYSErr | RxFIFOOver; +- else if (tp->mac_version == RTL_GIGA_MAC_VER_11) +- /* special workaround needed */ +- tp->irq_mask |= RxFIFOOver; + } + + static int rtl_alloc_irq(struct rtl8169_private *tp) +@@ -5321,7 +5310,6 @@ static int rtl_jumbo_max(struct rtl8169_ + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: + return JUMBO_7K; + /* RTL8168b */ +- case RTL_GIGA_MAC_VER_11: + case RTL_GIGA_MAC_VER_17: + return JUMBO_4K; + /* RTL8168c */ +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -276,15 +276,6 @@ static void rtl8169sce_hw_phy_config(str + rtl_writephy_batch(phydev, phy_reg_init); + } + +-static void rtl8168bb_hw_phy_config(struct rtl8169_private *tp, +- struct phy_device *phydev) +-{ +- phy_write(phydev, 0x1f, 0x0001); +- phy_set_bits(phydev, 0x16, BIT(0)); +- phy_write(phydev, 0x10, 0xf41b); +- phy_write(phydev, 0x1f, 0x0000); +-} +- + static void rtl8168bef_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) + { +@@ -1136,7 +1127,6 @@ void r8169_hw_phy_config(struct rtl8169_ + [RTL_GIGA_MAC_VER_08] = rtl8102e_hw_phy_config, + [RTL_GIGA_MAC_VER_09] = rtl8102e_hw_phy_config, + [RTL_GIGA_MAC_VER_10] = NULL, +- [RTL_GIGA_MAC_VER_11] = rtl8168bb_hw_phy_config, + [RTL_GIGA_MAC_VER_14] = rtl8401_hw_phy_config, + [RTL_GIGA_MAC_VER_17] = rtl8168bef_hw_phy_config, + [RTL_GIGA_MAC_VER_18] = rtl8168cp_1_hw_phy_config, diff --git a/target/linux/generic/backport-6.12/780-23-v6.14-r8169-adjust-version-numbering-for-RTL8126.patch b/target/linux/generic/backport-6.12/780-23-v6.14-r8169-adjust-version-numbering-for-RTL8126.patch new file mode 100644 index 00000000000..7ea47bd2ed7 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-23-v6.14-r8169-adjust-version-numbering-for-RTL8126.patch @@ -0,0 +1,257 @@ +From b299ea0069284186b0d3d54aebe87f0d195d457a Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Fri, 13 Dec 2024 20:01:41 +0100 +Subject: [PATCH] r8169: adjust version numbering for RTL8126 + +Adjust version numbering for RTL8126, so that it doesn't overlap with +new RTL8125 versions. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/6a354364-20e9-48ad-a198-468264288757@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169.h | 4 +- + drivers/net/ethernet/realtek/r8169_main.c | 62 +++++++++---------- + .../net/ethernet/realtek/r8169_phy_config.c | 4 +- + 3 files changed, 35 insertions(+), 35 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169.h ++++ b/drivers/net/ethernet/realtek/r8169.h +@@ -69,8 +69,8 @@ enum mac_version { + RTL_GIGA_MAC_VER_61, + RTL_GIGA_MAC_VER_63, + RTL_GIGA_MAC_VER_64, +- RTL_GIGA_MAC_VER_65, +- RTL_GIGA_MAC_VER_66, ++ RTL_GIGA_MAC_VER_70, ++ RTL_GIGA_MAC_VER_71, + RTL_GIGA_MAC_NONE + }; + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -139,8 +139,8 @@ static const struct { + /* reserve 62 for CFG_METHOD_4 in the vendor driver */ + [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2}, + [RTL_GIGA_MAC_VER_64] = {"RTL8125D", FIRMWARE_8125D_1}, +- [RTL_GIGA_MAC_VER_65] = {"RTL8126A", FIRMWARE_8126A_2}, +- [RTL_GIGA_MAC_VER_66] = {"RTL8126A", FIRMWARE_8126A_3}, ++ [RTL_GIGA_MAC_VER_70] = {"RTL8126A", FIRMWARE_8126A_2}, ++ [RTL_GIGA_MAC_VER_71] = {"RTL8126A", FIRMWARE_8126A_3}, + }; + + static const struct pci_device_id rtl8169_pci_tbl[] = { +@@ -1227,7 +1227,7 @@ static void rtl_writephy(struct rtl8169_ + case RTL_GIGA_MAC_VER_31: + r8168dp_2_mdio_write(tp, location, val); + break; +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_71: + r8168g_mdio_write(tp, location, val); + break; + default: +@@ -1242,7 +1242,7 @@ static int rtl_readphy(struct rtl8169_pr + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + return r8168dp_2_mdio_read(tp, location); +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_71: + return r8168g_mdio_read(tp, location); + default: + return r8169_mdio_read(tp, location); +@@ -1573,7 +1573,7 @@ static void __rtl8169_set_wol(struct rtl + break; + case RTL_GIGA_MAC_VER_34: + case RTL_GIGA_MAC_VER_37: +- case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_71: + r8169_mod_reg8_cond(tp, Config2, PME_SIGNAL, wolopts); + break; + default: +@@ -2046,7 +2046,7 @@ static void rtl_set_eee_txidle_timer(str + tp->tx_lpi_timer = timer_val; + r8168_mac_ocp_write(tp, 0xe048, timer_val); + break; +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: + tp->tx_lpi_timer = timer_val; + RTL_W16(tp, EEE_TXIDLE_TIMER_8125, timer_val); + break; +@@ -2254,8 +2254,8 @@ static enum mac_version rtl8169_get_mac_ + enum mac_version ver; + } mac_info[] = { + /* 8126A family. */ +- { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_66 }, +- { 0x7cf, 0x649, RTL_GIGA_MAC_VER_65 }, ++ { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_71 }, ++ { 0x7cf, 0x649, RTL_GIGA_MAC_VER_70 }, + + /* 8125D family. */ + { 0x7cf, 0x688, RTL_GIGA_MAC_VER_64 }, +@@ -2525,7 +2525,7 @@ static void rtl_init_rxcfg(struct rtl816 + case RTL_GIGA_MAC_VER_61: + RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST); + break; +- case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_71: + RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST | + RX_PAUSE_SLOT_ON); + break; +@@ -2657,7 +2657,7 @@ static void rtl_wait_txrx_fifo_empty(str + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61: + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + break; +- case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_71: + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond_2, 100, 42); +@@ -2923,7 +2923,7 @@ static void rtl_enable_exit_l1(struct rt + case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38: + rtl_eri_set_bits(tp, 0xd4, 0x0c00); + break; +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_71: + r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80); + break; + default: +@@ -2937,7 +2937,7 @@ static void rtl_disable_exit_l1(struct r + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38: + rtl_eri_clear_bits(tp, 0xd4, 0x1f00); + break; +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_71: + r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0); + break; + default: +@@ -2963,8 +2963,8 @@ static void rtl_hw_aspm_clkreq_enable(st + + rtl_mod_config5(tp, 0, ASPM_en); + switch (tp->mac_version) { +- case RTL_GIGA_MAC_VER_65: +- case RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_70: ++ case RTL_GIGA_MAC_VER_71: + val8 = RTL_R8(tp, INT_CFG0_8125) | INT_CFG0_CLKREQEN; + RTL_W8(tp, INT_CFG0_8125, val8); + break; +@@ -2975,7 +2975,7 @@ static void rtl_hw_aspm_clkreq_enable(st + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: + /* reset ephy tx/rx disable timer */ + r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0); + /* chip can trigger L1.2 */ +@@ -2987,7 +2987,7 @@ static void rtl_hw_aspm_clkreq_enable(st + } else { + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: + r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0); + break; + default: +@@ -2995,8 +2995,8 @@ static void rtl_hw_aspm_clkreq_enable(st + } + + switch (tp->mac_version) { +- case RTL_GIGA_MAC_VER_65: +- case RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_70: ++ case RTL_GIGA_MAC_VER_71: + val8 = RTL_R8(tp, INT_CFG0_8125) & ~INT_CFG0_CLKREQEN; + RTL_W8(tp, INT_CFG0_8125, val8); + break; +@@ -3716,12 +3716,12 @@ static void rtl_hw_start_8125_common(str + /* disable new tx descriptor format */ + r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000); + +- if (tp->mac_version == RTL_GIGA_MAC_VER_65 || +- tp->mac_version == RTL_GIGA_MAC_VER_66) ++ if (tp->mac_version == RTL_GIGA_MAC_VER_70 || ++ tp->mac_version == RTL_GIGA_MAC_VER_71) + RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02); + +- if (tp->mac_version == RTL_GIGA_MAC_VER_65 || +- tp->mac_version == RTL_GIGA_MAC_VER_66) ++ if (tp->mac_version == RTL_GIGA_MAC_VER_70 || ++ tp->mac_version == RTL_GIGA_MAC_VER_71) + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400); + else if (tp->mac_version == RTL_GIGA_MAC_VER_63) + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200); +@@ -3739,8 +3739,8 @@ static void rtl_hw_start_8125_common(str + r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0000); + r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000); + r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001); +- if (tp->mac_version == RTL_GIGA_MAC_VER_65 || +- tp->mac_version == RTL_GIGA_MAC_VER_66) ++ if (tp->mac_version == RTL_GIGA_MAC_VER_70 || ++ tp->mac_version == RTL_GIGA_MAC_VER_71) + r8168_mac_ocp_modify(tp, 0xea1c, 0x0300, 0x0000); + else + r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000); +@@ -3860,8 +3860,8 @@ static void rtl_hw_config(struct rtl8169 + [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2, + [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b, + [RTL_GIGA_MAC_VER_64] = rtl_hw_start_8125d, +- [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8126a, +- [RTL_GIGA_MAC_VER_66] = rtl_hw_start_8126a, ++ [RTL_GIGA_MAC_VER_70] = rtl_hw_start_8126a, ++ [RTL_GIGA_MAC_VER_71] = rtl_hw_start_8126a, + }; + + if (hw_configs[tp->mac_version]) +@@ -3882,8 +3882,8 @@ static void rtl_hw_start_8125(struct rtl + RTL_W32(tp, i, 0); + break; + case RTL_GIGA_MAC_VER_63: +- case RTL_GIGA_MAC_VER_65: +- case RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_70: ++ case RTL_GIGA_MAC_VER_71: + for (i = 0xa00; i < 0xa80; i += 4) + RTL_W32(tp, i, 0); + RTL_W16(tp, INT_CFG1_8125, 0x0000); +@@ -4115,7 +4115,7 @@ static void rtl8169_cleanup(struct rtl81 + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); + rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666); + break; +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_71: + rtl_enable_rxdvgate(tp); + fsleep(2000); + break; +@@ -4272,7 +4272,7 @@ static unsigned int rtl_quirk_packet_pad + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_34: +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: + padto = max_t(unsigned int, padto, ETH_ZLEN); + break; + default: +@@ -5291,7 +5291,7 @@ static void rtl_hw_initialize(struct rtl + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48: + rtl_hw_init_8168g(tp); + break; +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: + rtl_hw_init_8125(tp); + break; + default: +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -1162,8 +1162,8 @@ void r8169_hw_phy_config(struct rtl8169_ + [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config, + [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config, + [RTL_GIGA_MAC_VER_64] = rtl8125d_hw_phy_config, +- [RTL_GIGA_MAC_VER_65] = rtl8126a_hw_phy_config, +- [RTL_GIGA_MAC_VER_66] = rtl8126a_hw_phy_config, ++ [RTL_GIGA_MAC_VER_70] = rtl8126a_hw_phy_config, ++ [RTL_GIGA_MAC_VER_71] = rtl8126a_hw_phy_config, + }; + + if (phy_configs[ver]) diff --git a/target/linux/generic/backport-6.12/780-24-v6.14-r8169-add-support-for-RTL8125D-rev.b.patch b/target/linux/generic/backport-6.12/780-24-v6.14-r8169-add-support-for-RTL8125D-rev.b.patch new file mode 100644 index 00000000000..bbc447f4de0 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-24-v6.14-r8169-add-support-for-RTL8125D-rev.b.patch @@ -0,0 +1,90 @@ +From b3593df26ab19f114d613693fa8a92ab202803d0 Mon Sep 17 00:00:00 2001 +From: ChunHao Lin +Date: Fri, 13 Dec 2024 20:02:58 +0100 +Subject: [PATCH] r8169: add support for RTL8125D rev.b + +Add support for RTL8125D rev.b. Its XID is 0x689. It is basically +based on the one with XID 0x688, but with different firmware file. + +Signed-off-by: ChunHao Lin +[hkallweit1@gmail.com: rebased after adjusted version numbering] +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/75e5e9ec-d01f-43ac-b0f4-e7456baf18d1@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169.h | 1 + + drivers/net/ethernet/realtek/r8169_main.c | 6 ++++++ + drivers/net/ethernet/realtek/r8169_phy_config.c | 1 + + 3 files changed, 8 insertions(+) + +--- a/drivers/net/ethernet/realtek/r8169.h ++++ b/drivers/net/ethernet/realtek/r8169.h +@@ -69,6 +69,7 @@ enum mac_version { + RTL_GIGA_MAC_VER_61, + RTL_GIGA_MAC_VER_63, + RTL_GIGA_MAC_VER_64, ++ RTL_GIGA_MAC_VER_65, + RTL_GIGA_MAC_VER_70, + RTL_GIGA_MAC_VER_71, + RTL_GIGA_MAC_NONE +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -56,6 +56,7 @@ + #define FIRMWARE_8125A_3 "rtl_nic/rtl8125a-3.fw" + #define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw" + #define FIRMWARE_8125D_1 "rtl_nic/rtl8125d-1.fw" ++#define FIRMWARE_8125D_2 "rtl_nic/rtl8125d-2.fw" + #define FIRMWARE_8126A_2 "rtl_nic/rtl8126a-2.fw" + #define FIRMWARE_8126A_3 "rtl_nic/rtl8126a-3.fw" + +@@ -139,6 +140,7 @@ static const struct { + /* reserve 62 for CFG_METHOD_4 in the vendor driver */ + [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2}, + [RTL_GIGA_MAC_VER_64] = {"RTL8125D", FIRMWARE_8125D_1}, ++ [RTL_GIGA_MAC_VER_65] = {"RTL8125D", FIRMWARE_8125D_2}, + [RTL_GIGA_MAC_VER_70] = {"RTL8126A", FIRMWARE_8126A_2}, + [RTL_GIGA_MAC_VER_71] = {"RTL8126A", FIRMWARE_8126A_3}, + }; +@@ -705,6 +707,7 @@ MODULE_FIRMWARE(FIRMWARE_8107E_2); + MODULE_FIRMWARE(FIRMWARE_8125A_3); + MODULE_FIRMWARE(FIRMWARE_8125B_2); + MODULE_FIRMWARE(FIRMWARE_8125D_1); ++MODULE_FIRMWARE(FIRMWARE_8125D_2); + MODULE_FIRMWARE(FIRMWARE_8126A_2); + MODULE_FIRMWARE(FIRMWARE_8126A_3); + +@@ -2258,6 +2261,7 @@ static enum mac_version rtl8169_get_mac_ + { 0x7cf, 0x649, RTL_GIGA_MAC_VER_70 }, + + /* 8125D family. */ ++ { 0x7cf, 0x689, RTL_GIGA_MAC_VER_65 }, + { 0x7cf, 0x688, RTL_GIGA_MAC_VER_64 }, + + /* 8125B family. */ +@@ -3860,6 +3864,7 @@ static void rtl_hw_config(struct rtl8169 + [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2, + [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b, + [RTL_GIGA_MAC_VER_64] = rtl_hw_start_8125d, ++ [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_70] = rtl_hw_start_8126a, + [RTL_GIGA_MAC_VER_71] = rtl_hw_start_8126a, + }; +@@ -3878,6 +3883,7 @@ static void rtl_hw_start_8125(struct rtl + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_61: + case RTL_GIGA_MAC_VER_64: ++ case RTL_GIGA_MAC_VER_65: + for (i = 0xa00; i < 0xb00; i += 4) + RTL_W32(tp, i, 0); + break; +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -1162,6 +1162,7 @@ void r8169_hw_phy_config(struct rtl8169_ + [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config, + [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config, + [RTL_GIGA_MAC_VER_64] = rtl8125d_hw_phy_config, ++ [RTL_GIGA_MAC_VER_65] = rtl8125d_hw_phy_config, + [RTL_GIGA_MAC_VER_70] = rtl8126a_hw_phy_config, + [RTL_GIGA_MAC_VER_71] = rtl8126a_hw_phy_config, + }; diff --git a/target/linux/generic/backport-6.12/780-25-v6.14-r8169-add-support-for-RTL8125BP-rev.b.patch b/target/linux/generic/backport-6.12/780-25-v6.14-r8169-add-support-for-RTL8125BP-rev.b.patch new file mode 100644 index 00000000000..7b4f474bee8 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-25-v6.14-r8169-add-support-for-RTL8125BP-rev.b.patch @@ -0,0 +1,184 @@ +From b11bff90f2ad52c5c55c822ecd20326619a73898 Mon Sep 17 00:00:00 2001 +From: ChunHao Lin +Date: Tue, 7 Jan 2025 14:43:55 +0800 +Subject: [PATCH] r8169: add support for RTL8125BP rev.b + +Add support for RTL8125BP rev.b. Its XID is 0x689. This chip supports +DASH and its dash type is "RTL_DASH_25_BP". + +Signed-off-by: ChunHao Lin +Reviewed-by: Heiner Kallweit +Link: https://patch.msgid.link/20250107064355.104711-1-hau@realtek.com +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/realtek/r8169.h | 1 + + drivers/net/ethernet/realtek/r8169_main.c | 30 +++++++++++++++++++ + .../net/ethernet/realtek/r8169_phy_config.c | 23 ++++++++++++++ + 3 files changed, 54 insertions(+) + +--- a/drivers/net/ethernet/realtek/r8169.h ++++ b/drivers/net/ethernet/realtek/r8169.h +@@ -70,6 +70,7 @@ enum mac_version { + RTL_GIGA_MAC_VER_63, + RTL_GIGA_MAC_VER_64, + RTL_GIGA_MAC_VER_65, ++ RTL_GIGA_MAC_VER_66, + RTL_GIGA_MAC_VER_70, + RTL_GIGA_MAC_VER_71, + RTL_GIGA_MAC_NONE +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -57,6 +57,7 @@ + #define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw" + #define FIRMWARE_8125D_1 "rtl_nic/rtl8125d-1.fw" + #define FIRMWARE_8125D_2 "rtl_nic/rtl8125d-2.fw" ++#define FIRMWARE_8125BP_2 "rtl_nic/rtl8125bp-2.fw" + #define FIRMWARE_8126A_2 "rtl_nic/rtl8126a-2.fw" + #define FIRMWARE_8126A_3 "rtl_nic/rtl8126a-3.fw" + +@@ -141,6 +142,7 @@ static const struct { + [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2}, + [RTL_GIGA_MAC_VER_64] = {"RTL8125D", FIRMWARE_8125D_1}, + [RTL_GIGA_MAC_VER_65] = {"RTL8125D", FIRMWARE_8125D_2}, ++ [RTL_GIGA_MAC_VER_66] = {"RTL8125BP", FIRMWARE_8125BP_2}, + [RTL_GIGA_MAC_VER_70] = {"RTL8126A", FIRMWARE_8126A_2}, + [RTL_GIGA_MAC_VER_71] = {"RTL8126A", FIRMWARE_8126A_3}, + }; +@@ -631,6 +633,7 @@ enum rtl_dash_type { + RTL_DASH_NONE, + RTL_DASH_DP, + RTL_DASH_EP, ++ RTL_DASH_25_BP, + }; + + struct rtl8169_private { +@@ -708,6 +711,7 @@ MODULE_FIRMWARE(FIRMWARE_8125A_3); + MODULE_FIRMWARE(FIRMWARE_8125B_2); + MODULE_FIRMWARE(FIRMWARE_8125D_1); + MODULE_FIRMWARE(FIRMWARE_8125D_2); ++MODULE_FIRMWARE(FIRMWARE_8125BP_2); + MODULE_FIRMWARE(FIRMWARE_8126A_2); + MODULE_FIRMWARE(FIRMWARE_8126A_3); + +@@ -1360,10 +1364,19 @@ static void rtl8168ep_driver_start(struc + rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 30); + } + ++static void rtl8125bp_driver_start(struct rtl8169_private *tp) ++{ ++ r8168ep_ocp_write(tp, 0x01, 0x14, OOB_CMD_DRIVER_START); ++ r8168ep_ocp_write(tp, 0x01, 0x18, 0x00); ++ r8168ep_ocp_write(tp, 0x01, 0x10, 0x01); ++} ++ + static void rtl8168_driver_start(struct rtl8169_private *tp) + { + if (tp->dash_type == RTL_DASH_DP) + rtl8168dp_driver_start(tp); ++ else if (tp->dash_type == RTL_DASH_25_BP) ++ rtl8125bp_driver_start(tp); + else + rtl8168ep_driver_start(tp); + } +@@ -1384,10 +1397,19 @@ static void rtl8168ep_driver_stop(struct + rtl_loop_wait_low(tp, &rtl_ep_ocp_read_cond, 10000, 10); + } + ++static void rtl8125bp_driver_stop(struct rtl8169_private *tp) ++{ ++ r8168ep_ocp_write(tp, 0x01, 0x14, OOB_CMD_DRIVER_STOP); ++ r8168ep_ocp_write(tp, 0x01, 0x18, 0x00); ++ r8168ep_ocp_write(tp, 0x01, 0x10, 0x01); ++} ++ + static void rtl8168_driver_stop(struct rtl8169_private *tp) + { + if (tp->dash_type == RTL_DASH_DP) + rtl8168dp_driver_stop(tp); ++ else if (tp->dash_type == RTL_DASH_25_BP) ++ rtl8125bp_driver_stop(tp); + else + rtl8168ep_driver_stop(tp); + } +@@ -1410,6 +1432,7 @@ static bool rtl_dash_is_enabled(struct r + case RTL_DASH_DP: + return r8168dp_check_dash(tp); + case RTL_DASH_EP: ++ case RTL_DASH_25_BP: + return r8168ep_check_dash(tp); + default: + return false; +@@ -1424,6 +1447,8 @@ static enum rtl_dash_type rtl_get_dash_t + return RTL_DASH_DP; + case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53: + return RTL_DASH_EP; ++ case RTL_GIGA_MAC_VER_66: ++ return RTL_DASH_25_BP; + default: + return RTL_DASH_NONE; + } +@@ -2260,6 +2285,9 @@ static enum mac_version rtl8169_get_mac_ + { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_71 }, + { 0x7cf, 0x649, RTL_GIGA_MAC_VER_70 }, + ++ /* 8125BP family. */ ++ { 0x7cf, 0x681, RTL_GIGA_MAC_VER_66 }, ++ + /* 8125D family. */ + { 0x7cf, 0x689, RTL_GIGA_MAC_VER_65 }, + { 0x7cf, 0x688, RTL_GIGA_MAC_VER_64 }, +@@ -3865,6 +3893,7 @@ static void rtl_hw_config(struct rtl8169 + [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b, + [RTL_GIGA_MAC_VER_64] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8125d, ++ [RTL_GIGA_MAC_VER_66] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_70] = rtl_hw_start_8126a, + [RTL_GIGA_MAC_VER_71] = rtl_hw_start_8126a, + }; +@@ -3884,6 +3913,7 @@ static void rtl_hw_start_8125(struct rtl + case RTL_GIGA_MAC_VER_61: + case RTL_GIGA_MAC_VER_64: + case RTL_GIGA_MAC_VER_65: ++ case RTL_GIGA_MAC_VER_66: + for (i = 0xa00; i < 0xb00; i += 4) + RTL_W32(tp, i, 0); + break; +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -1102,6 +1102,28 @@ static void rtl8125d_hw_phy_config(struc + rtl8125_config_eee_phy(phydev); + } + ++static void rtl8125bp_hw_phy_config(struct rtl8169_private *tp, ++ struct phy_device *phydev) ++{ ++ r8169_apply_firmware(tp); ++ rtl8168g_enable_gphy_10m(phydev); ++ ++ r8168g_phy_param(phydev, 0x8010, 0x0800, 0x0000); ++ ++ phy_write(phydev, 0x1f, 0x0b87); ++ phy_write(phydev, 0x16, 0x8088); ++ phy_modify(phydev, 0x17, 0xff00, 0x9000); ++ phy_write(phydev, 0x16, 0x808f); ++ phy_modify(phydev, 0x17, 0xff00, 0x9000); ++ phy_write(phydev, 0x1f, 0x0000); ++ ++ r8168g_phy_param(phydev, 0x8174, 0x2000, 0x1800); ++ ++ rtl8125_legacy_force_mode(phydev); ++ rtl8168g_disable_aldps(phydev); ++ rtl8125_config_eee_phy(phydev); ++} ++ + static void rtl8126a_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) + { +@@ -1163,6 +1185,7 @@ void r8169_hw_phy_config(struct rtl8169_ + [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config, + [RTL_GIGA_MAC_VER_64] = rtl8125d_hw_phy_config, + [RTL_GIGA_MAC_VER_65] = rtl8125d_hw_phy_config, ++ [RTL_GIGA_MAC_VER_66] = rtl8125bp_hw_phy_config, + [RTL_GIGA_MAC_VER_70] = rtl8126a_hw_phy_config, + [RTL_GIGA_MAC_VER_71] = rtl8126a_hw_phy_config, + }; diff --git a/target/linux/generic/backport-6.12/780-26-v6.15-r8169-make-Kconfig-option-for-LED-support-user-visib.patch b/target/linux/generic/backport-6.12/780-26-v6.15-r8169-make-Kconfig-option-for-LED-support-user-visib.patch new file mode 100644 index 00000000000..62acd269639 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-26-v6.15-r8169-make-Kconfig-option-for-LED-support-user-visib.patch @@ -0,0 +1,28 @@ +From 135c3c86a7cef4ba3d368da15b16c275b74582d3 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Mon, 3 Feb 2025 21:35:24 +0100 +Subject: [PATCH] r8169: make Kconfig option for LED support user-visible + +Make config option R8169_LEDS user-visible, so that users can remove +support if not needed. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/d29f0cdb-32bf-435f-b59d-dc96bca1e3ab@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/Kconfig | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/realtek/Kconfig ++++ b/drivers/net/ethernet/realtek/Kconfig +@@ -114,7 +114,8 @@ config R8169 + will be called r8169. This is recommended. + + config R8169_LEDS +- def_bool R8169 && LEDS_TRIGGER_NETDEV ++ bool "Support for controlling the NIC LEDs" ++ depends on R8169 && LEDS_TRIGGER_NETDEV + depends on !(R8169=y && LEDS_CLASS=m) + help + Optional support for controlling the NIC LED's with the netdev diff --git a/target/linux/generic/backport-6.12/780-28-v6.15-r8169-add-support-for-Intel-Killer-E5000.patch b/target/linux/generic/backport-6.12/780-28-v6.15-r8169-add-support-for-Intel-Killer-E5000.patch new file mode 100644 index 00000000000..d741d193ab5 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-28-v6.15-r8169-add-support-for-Intel-Killer-E5000.patch @@ -0,0 +1,25 @@ +From d30460f42675fef5cd4b44ffbc49b545524555e3 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 12 Feb 2025 08:03:56 +0100 +Subject: [PATCH] r8169: add support for Intel Killer E5000 + +This adds support for the Intel Killer E5000 which seems to be a +rebranded RTL8126. Copied from r8126 vendor driver. + +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/9db73e9b-e2e8-45de-97a5-041c5f71d774@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -169,6 +169,7 @@ static const struct pci_device_id rtl816 + { PCI_VDEVICE(REALTEK, 0x8125) }, + { PCI_VDEVICE(REALTEK, 0x8126) }, + { PCI_VDEVICE(REALTEK, 0x3000) }, ++ { PCI_VDEVICE(REALTEK, 0x5000) }, + {} + }; + diff --git a/target/linux/generic/backport-6.12/780-29-v6.15-r8169-add-PHY-c45-ops-for-MDIO_MMD_VENDOR2-registers.patch b/target/linux/generic/backport-6.12/780-29-v6.15-r8169-add-PHY-c45-ops-for-MDIO_MMD_VENDOR2-registers.patch new file mode 100644 index 00000000000..1336ec6f916 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-29-v6.15-r8169-add-PHY-c45-ops-for-MDIO_MMD_VENDOR2-registers.patch @@ -0,0 +1,67 @@ +From 853e80369cfceb2331bf34f251ba11c6602cc67f Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Thu, 13 Feb 2025 20:15:42 +0100 +Subject: [PATCH] r8169: add PHY c45 ops for MDIO_MMD_VENDOR2 registers + +The integrated PHYs on chip versions from RTL8168g allow to address +MDIO_MMD_VEND2 registers. All c22 standard registers are mapped to +MDIO_MMD_VEND2 registers. So far the paging mechanism is used to +address PHY registers. Add support for c45 ops to address MDIO_MMD_VEND2 +registers directly, w/o the paging. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/d6f97eaa-0f13-468f-89cb-75a41087bc4a@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 32 +++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -5225,6 +5225,33 @@ static int r8169_mdio_write_reg(struct m + return 0; + } + ++static int r8169_mdio_read_reg_c45(struct mii_bus *mii_bus, int addr, ++ int devnum, int regnum) ++{ ++ struct rtl8169_private *tp = mii_bus->priv; ++ ++ if (addr > 0) ++ return -ENODEV; ++ ++ if (devnum == MDIO_MMD_VEND2 && regnum > MDIO_STAT2) ++ return r8168_phy_ocp_read(tp, regnum); ++ ++ return 0; ++} ++ ++static int r8169_mdio_write_reg_c45(struct mii_bus *mii_bus, int addr, ++ int devnum, int regnum, u16 val) ++{ ++ struct rtl8169_private *tp = mii_bus->priv; ++ ++ if (addr > 0 || devnum != MDIO_MMD_VEND2 || regnum <= MDIO_STAT2) ++ return -ENODEV; ++ ++ r8168_phy_ocp_write(tp, regnum, val); ++ ++ return 0; ++} ++ + static int r8169_mdio_register(struct rtl8169_private *tp) + { + struct pci_dev *pdev = tp->pci_dev; +@@ -5255,6 +5282,11 @@ static int r8169_mdio_register(struct rt + new_bus->read = r8169_mdio_read_reg; + new_bus->write = r8169_mdio_write_reg; + ++ if (tp->mac_version >= RTL_GIGA_MAC_VER_40) { ++ new_bus->read_c45 = r8169_mdio_read_reg_c45; ++ new_bus->write_c45 = r8169_mdio_write_reg_c45; ++ } ++ + ret = devm_mdiobus_register(&pdev->dev, new_bus); + if (ret) + return ret; diff --git a/target/linux/generic/backport-6.12/780-30-v6.15-r8169-increase-max-jumbo-packet-size-on-RTL8125-RTL8.patch b/target/linux/generic/backport-6.12/780-30-v6.15-r8169-increase-max-jumbo-packet-size-on-RTL8125-RTL8.patch new file mode 100644 index 00000000000..852c9a3da7e --- /dev/null +++ b/target/linux/generic/backport-6.12/780-30-v6.15-r8169-increase-max-jumbo-packet-size-on-RTL8125-RTL8.patch @@ -0,0 +1,40 @@ +From 473367a5ffe1607a61be481e2feda684eb5faea9 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Fri, 7 Mar 2025 08:29:47 +0100 +Subject: [PATCH] r8169: increase max jumbo packet size on RTL8125/RTL8126 + +Realtek confirmed that all RTL8125/RTL8126 chip versions support up to +16K jumbo packets. Reflect this in the driver. + +Tested by Rui on RTL8125B with 12K jumbo packets. + +Suggested-by: Rui Salvaterra +Tested-by: Rui Salvaterra +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/396762ad-cc65-4e60-b01e-8847db89e98b@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -89,6 +89,7 @@ + #define JUMBO_6K (6 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) + #define JUMBO_7K (7 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) + #define JUMBO_9K (9 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) ++#define JUMBO_16K (SZ_16K - VLAN_ETH_HLEN - ETH_FCS_LEN) + + static const struct { + const char *name; +@@ -5384,6 +5385,9 @@ static int rtl_jumbo_max(struct rtl8169_ + /* RTL8168c */ + case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_24: + return JUMBO_6K; ++ /* RTL8125/8126 */ ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: ++ return JUMBO_16K; + default: + return JUMBO_9K; + } diff --git a/target/linux/generic/backport-6.12/780-31-v6.15-r8169-switch-away-from-deprecated-pcim_iomap_table.patch b/target/linux/generic/backport-6.12/780-31-v6.15-r8169-switch-away-from-deprecated-pcim_iomap_table.patch new file mode 100644 index 00000000000..07212475086 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-31-v6.15-r8169-switch-away-from-deprecated-pcim_iomap_table.patch @@ -0,0 +1,35 @@ +From 34e5ededf4b8ad4c9e58f0cab8596e26c8fa59a2 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 12 Mar 2025 20:21:42 +0100 +Subject: [PATCH] r8169: switch away from deprecated pcim_iomap_table + +Avoid using deprecated pcim_iomap_table by switching to +pcim_iomap_region. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Jacob Keller +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/a36b4cf3-c792-40fa-8164-5dc9d5f14dd0@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/realtek/r8169_main.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -5471,11 +5471,10 @@ static int rtl_init_one(struct pci_dev * + if (region < 0) + return dev_err_probe(&pdev->dev, -ENODEV, "no MMIO resource found\n"); + +- rc = pcim_iomap_regions(pdev, BIT(region), KBUILD_MODNAME); +- if (rc < 0) +- return dev_err_probe(&pdev->dev, rc, "cannot remap MMIO, aborting\n"); +- +- tp->mmio_addr = pcim_iomap_table(pdev)[region]; ++ tp->mmio_addr = pcim_iomap_region(pdev, region, KBUILD_MODNAME); ++ if (IS_ERR(tp->mmio_addr)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(tp->mmio_addr), ++ "cannot remap MMIO, aborting\n"); + + txconfig = RTL_R32(tp, TxConfig); + if (txconfig == ~0U) diff --git a/target/linux/generic/backport-6.12/780-32-v6.15-r8169-enable-RTL8168H-RTL8168EP-RTL8168FP-ASPM-suppo.patch b/target/linux/generic/backport-6.12/780-32-v6.15-r8169-enable-RTL8168H-RTL8168EP-RTL8168FP-ASPM-suppo.patch new file mode 100644 index 00000000000..331d296355e --- /dev/null +++ b/target/linux/generic/backport-6.12/780-32-v6.15-r8169-enable-RTL8168H-RTL8168EP-RTL8168FP-ASPM-suppo.patch @@ -0,0 +1,27 @@ +From 3d9b8ac5341269d31e59fd5d58d47266ac78bc32 Mon Sep 17 00:00:00 2001 +From: ChunHao Lin +Date: Tue, 18 Mar 2025 16:37:20 +0800 +Subject: [PATCH] r8169: enable RTL8168H/RTL8168EP/RTL8168FP ASPM support + +This patch will enable RTL8168H/RTL8168EP/RTL8168FP ASPM support on +the platforms that have tested with ASPM enabled. + +Signed-off-by: ChunHao Lin +Reviewed-by: Heiner Kallweit +Link: https://patch.msgid.link/20250318083721.4127-2-hau@realtek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -5422,7 +5422,7 @@ done: + /* register is set if system vendor successfully tested ASPM 1.2 */ + static bool rtl_aspm_is_safe(struct rtl8169_private *tp) + { +- if (tp->mac_version >= RTL_GIGA_MAC_VER_61 && ++ if (tp->mac_version >= RTL_GIGA_MAC_VER_46 && + r8168_mac_ocp_read(tp, 0xc0b2) & 0xf) + return true; + diff --git a/target/linux/generic/backport-6.12/780-34-v6.16-r8169-add-helper-rtl_csi_mod-for-accessing-extended.patch b/target/linux/generic/backport-6.12/780-34-v6.16-r8169-add-helper-rtl_csi_mod-for-accessing-extended.patch new file mode 100644 index 00000000000..383f9eb5f7b --- /dev/null +++ b/target/linux/generic/backport-6.12/780-34-v6.16-r8169-add-helper-rtl_csi_mod-for-accessing-extended.patch @@ -0,0 +1,74 @@ +From 8c40d99e5f43e0545a3f4fea9156313847e2eb79 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 9 Apr 2025 21:05:37 +0200 +Subject: [PATCH] r8169: add helper rtl_csi_mod for accessing extended config + space + +Add a helper for the Realtek-specific mechanism for accessing extended +config space if native access isn't possible. +This avoids code duplication. + +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/b368fd91-57d7-4cb5-9342-98b4d8fe9aea@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 26 ++++++++++++++--------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -2849,10 +2849,23 @@ static u32 rtl_csi_read(struct rtl8169_p + RTL_R32(tp, CSIDR) : ~0; + } + ++static void rtl_csi_mod(struct rtl8169_private *tp, int addr, ++ u32 mask, u32 set) ++{ ++ u32 val; ++ ++ WARN(addr % 4, "Invalid CSI address %#x\n", addr); ++ ++ netdev_notice_once(tp->dev, ++ "No native access to PCI extended config space, falling back to CSI\n"); ++ ++ val = rtl_csi_read(tp, addr); ++ rtl_csi_write(tp, addr, (val & ~mask) | set); ++} ++ + static void rtl_disable_zrxdc_timeout(struct rtl8169_private *tp) + { + struct pci_dev *pdev = tp->pci_dev; +- u32 csi; + int rc; + u8 val; + +@@ -2869,16 +2882,12 @@ static void rtl_disable_zrxdc_timeout(st + } + } + +- netdev_notice_once(tp->dev, +- "No native access to PCI extended config space, falling back to CSI\n"); +- csi = rtl_csi_read(tp, RTL_GEN3_RELATED_OFF); +- rtl_csi_write(tp, RTL_GEN3_RELATED_OFF, csi & ~RTL_GEN3_ZRXDC_NONCOMPL); ++ rtl_csi_mod(tp, RTL_GEN3_RELATED_OFF, RTL_GEN3_ZRXDC_NONCOMPL, 0); + } + + static void rtl_set_aspm_entry_latency(struct rtl8169_private *tp, u8 val) + { + struct pci_dev *pdev = tp->pci_dev; +- u32 csi; + + /* According to Realtek the value at config space address 0x070f + * controls the L0s/L1 entrance latency. We try standard ECAM access +@@ -2890,10 +2899,7 @@ static void rtl_set_aspm_entry_latency(s + pci_write_config_byte(pdev, 0x070f, val) == PCIBIOS_SUCCESSFUL) + return; + +- netdev_notice_once(tp->dev, +- "No native access to PCI extended config space, falling back to CSI\n"); +- csi = rtl_csi_read(tp, 0x070c) & 0x00ffffff; +- rtl_csi_write(tp, 0x070c, csi | val << 24); ++ rtl_csi_mod(tp, 0x070c, 0xff000000, val << 24); + } + + static void rtl_set_def_aspm_entry_latency(struct rtl8169_private *tp) diff --git a/target/linux/generic/backport-6.12/780-35-v6.16-r8169-add-helper-rtl8125_phy_param.patch b/target/linux/generic/backport-6.12/780-35-v6.16-r8169-add-helper-rtl8125_phy_param.patch new file mode 100644 index 00000000000..30ebb4b6f48 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-35-v6.16-r8169-add-helper-rtl8125_phy_param.patch @@ -0,0 +1,82 @@ +From 0c49baf099ba2147a6ff3bbdc3197c6ddbee5469 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Wed, 9 Apr 2025 21:14:47 +0200 +Subject: [PATCH] r8169: add helper rtl8125_phy_param + +The integrated PHY's of RTL8125/8126 have an own mechanism to access +PHY parameters, similar to what r8168g_phy_param does on earlier PHY +versions. Add helper rtl8125_phy_param to simplify the code. + +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/847b7356-12d6-441b-ade9-4b6e1539b84a@gmail.com +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/realtek/r8169_phy_config.c | 36 +++++++++---------- + 1 file changed, 16 insertions(+), 20 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -50,6 +50,15 @@ static void r8168g_phy_param(struct phy_ + phy_restore_page(phydev, oldpage, 0); + } + ++static void rtl8125_phy_param(struct phy_device *phydev, u16 parm, ++ u16 mask, u16 val) ++{ ++ phy_lock_mdio_bus(phydev); ++ __phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xb87c, parm); ++ __phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xb87e, mask, val); ++ phy_unlock_mdio_bus(phydev); ++} ++ + struct phy_reg { + u16 reg; + u16 val; +@@ -1004,12 +1013,8 @@ static void rtl8125a_2_hw_phy_config(str + phy_write_paged(phydev, 0xac5, 0x16, 0x01ff); + phy_modify_paged(phydev, 0xac8, 0x15, 0x00f0, 0x0030); + +- phy_write(phydev, 0x1f, 0x0b87); +- phy_write(phydev, 0x16, 0x80a2); +- phy_write(phydev, 0x17, 0x0153); +- phy_write(phydev, 0x16, 0x809c); +- phy_write(phydev, 0x17, 0x0153); +- phy_write(phydev, 0x1f, 0x0000); ++ rtl8125_phy_param(phydev, 0x80a2, 0xffff, 0x0153); ++ rtl8125_phy_param(phydev, 0x809c, 0xffff, 0x0153); + + phy_write(phydev, 0x1f, 0x0a43); + phy_write(phydev, 0x13, 0x81B3); +@@ -1061,14 +1066,9 @@ static void rtl8125b_hw_phy_config(struc + phy_modify_paged(phydev, 0xac4, 0x13, 0x00f0, 0x0090); + phy_modify_paged(phydev, 0xad3, 0x10, 0x0003, 0x0001); + +- phy_write(phydev, 0x1f, 0x0b87); +- phy_write(phydev, 0x16, 0x80f5); +- phy_write(phydev, 0x17, 0x760e); +- phy_write(phydev, 0x16, 0x8107); +- phy_write(phydev, 0x17, 0x360e); +- phy_write(phydev, 0x16, 0x8551); +- phy_modify(phydev, 0x17, 0xff00, 0x0800); +- phy_write(phydev, 0x1f, 0x0000); ++ rtl8125_phy_param(phydev, 0x80f5, 0xffff, 0x760e); ++ rtl8125_phy_param(phydev, 0x8107, 0xffff, 0x360e); ++ rtl8125_phy_param(phydev, 0x8551, 0xff00, 0x0800); + + phy_modify_paged(phydev, 0xbf0, 0x10, 0xe000, 0xa000); + phy_modify_paged(phydev, 0xbf4, 0x13, 0x0f00, 0x0300); +@@ -1110,12 +1110,8 @@ static void rtl8125bp_hw_phy_config(stru + + r8168g_phy_param(phydev, 0x8010, 0x0800, 0x0000); + +- phy_write(phydev, 0x1f, 0x0b87); +- phy_write(phydev, 0x16, 0x8088); +- phy_modify(phydev, 0x17, 0xff00, 0x9000); +- phy_write(phydev, 0x16, 0x808f); +- phy_modify(phydev, 0x17, 0xff00, 0x9000); +- phy_write(phydev, 0x1f, 0x0000); ++ rtl8125_phy_param(phydev, 0x8088, 0xff00, 0x9000); ++ rtl8125_phy_param(phydev, 0x808f, 0xff00, 0x9000); + + r8168g_phy_param(phydev, 0x8174, 0x2000, 0x1800); + diff --git a/target/linux/generic/backport-6.12/780-36-v6.16-r8169-refactor-chip-version-detection.patch b/target/linux/generic/backport-6.12/780-36-v6.16-r8169-refactor-chip-version-detection.patch new file mode 100644 index 00000000000..ae9cfb8a660 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-36-v6.16-r8169-refactor-chip-version-detection.patch @@ -0,0 +1,401 @@ +From 2b065c098c37a3ed28df7c3be59dca61b9da8402 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Tue, 15 Apr 2025 21:29:34 +0200 +Subject: [PATCH] r8169: refactor chip version detection + +Refactor chip version detection and merge both configuration tables. +Apart from reducing the code by a third, this paves the way for +merging chip version handling if only difference is the firmware. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Michal Swiatkowski +Link: https://patch.msgid.link/1fea533a-dd5a-4198-a9e2-895e11083947@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 325 +++++++++------------- + 1 file changed, 128 insertions(+), 197 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -91,61 +91,114 @@ + #define JUMBO_9K (9 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) + #define JUMBO_16K (SZ_16K - VLAN_ETH_HLEN - ETH_FCS_LEN) + +-static const struct { ++static const struct rtl_chip_info { ++ u16 mask; ++ u16 val; ++ enum mac_version mac_version; + const char *name; + const char *fw_name; + } rtl_chip_infos[] = { +- /* PCI devices. */ +- [RTL_GIGA_MAC_VER_02] = {"RTL8169s" }, +- [RTL_GIGA_MAC_VER_03] = {"RTL8110s" }, +- [RTL_GIGA_MAC_VER_04] = {"RTL8169sb/8110sb" }, +- [RTL_GIGA_MAC_VER_05] = {"RTL8169sc/8110sc" }, +- [RTL_GIGA_MAC_VER_06] = {"RTL8169sc/8110sc" }, +- /* PCI-E devices. */ +- [RTL_GIGA_MAC_VER_07] = {"RTL8102e" }, +- [RTL_GIGA_MAC_VER_08] = {"RTL8102e" }, +- [RTL_GIGA_MAC_VER_09] = {"RTL8102e/RTL8103e" }, +- [RTL_GIGA_MAC_VER_10] = {"RTL8101e/RTL8100e" }, +- [RTL_GIGA_MAC_VER_14] = {"RTL8401" }, +- [RTL_GIGA_MAC_VER_17] = {"RTL8168b/8111b" }, +- [RTL_GIGA_MAC_VER_18] = {"RTL8168cp/8111cp" }, +- [RTL_GIGA_MAC_VER_19] = {"RTL8168c/8111c" }, +- [RTL_GIGA_MAC_VER_20] = {"RTL8168c/8111c" }, +- [RTL_GIGA_MAC_VER_21] = {"RTL8168c/8111c" }, +- [RTL_GIGA_MAC_VER_22] = {"RTL8168c/8111c" }, +- [RTL_GIGA_MAC_VER_23] = {"RTL8168cp/8111cp" }, +- [RTL_GIGA_MAC_VER_24] = {"RTL8168cp/8111cp" }, +- [RTL_GIGA_MAC_VER_25] = {"RTL8168d/8111d", FIRMWARE_8168D_1}, +- [RTL_GIGA_MAC_VER_26] = {"RTL8168d/8111d", FIRMWARE_8168D_2}, +- [RTL_GIGA_MAC_VER_28] = {"RTL8168dp/8111dp" }, +- [RTL_GIGA_MAC_VER_29] = {"RTL8105e", FIRMWARE_8105E_1}, +- [RTL_GIGA_MAC_VER_30] = {"RTL8105e", FIRMWARE_8105E_1}, +- [RTL_GIGA_MAC_VER_31] = {"RTL8168dp/8111dp" }, +- [RTL_GIGA_MAC_VER_32] = {"RTL8168e/8111e", FIRMWARE_8168E_1}, +- [RTL_GIGA_MAC_VER_33] = {"RTL8168e/8111e", FIRMWARE_8168E_2}, +- [RTL_GIGA_MAC_VER_34] = {"RTL8168evl/8111evl", FIRMWARE_8168E_3}, +- [RTL_GIGA_MAC_VER_35] = {"RTL8168f/8111f", FIRMWARE_8168F_1}, +- [RTL_GIGA_MAC_VER_36] = {"RTL8168f/8111f", FIRMWARE_8168F_2}, +- [RTL_GIGA_MAC_VER_37] = {"RTL8402", FIRMWARE_8402_1 }, +- [RTL_GIGA_MAC_VER_38] = {"RTL8411", FIRMWARE_8411_1 }, +- [RTL_GIGA_MAC_VER_39] = {"RTL8106e", FIRMWARE_8106E_1}, +- [RTL_GIGA_MAC_VER_40] = {"RTL8168g/8111g", FIRMWARE_8168G_2}, +- [RTL_GIGA_MAC_VER_42] = {"RTL8168gu/8111gu", FIRMWARE_8168G_3}, +- [RTL_GIGA_MAC_VER_43] = {"RTL8106eus", FIRMWARE_8106E_2}, +- [RTL_GIGA_MAC_VER_44] = {"RTL8411b", FIRMWARE_8411_2 }, +- [RTL_GIGA_MAC_VER_46] = {"RTL8168h/8111h", FIRMWARE_8168H_2}, +- [RTL_GIGA_MAC_VER_48] = {"RTL8107e", FIRMWARE_8107E_2}, +- [RTL_GIGA_MAC_VER_51] = {"RTL8168ep/8111ep" }, +- [RTL_GIGA_MAC_VER_52] = {"RTL8168fp/RTL8117", FIRMWARE_8168FP_3}, +- [RTL_GIGA_MAC_VER_53] = {"RTL8168fp/RTL8117", }, +- [RTL_GIGA_MAC_VER_61] = {"RTL8125A", FIRMWARE_8125A_3}, +- /* reserve 62 for CFG_METHOD_4 in the vendor driver */ +- [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2}, +- [RTL_GIGA_MAC_VER_64] = {"RTL8125D", FIRMWARE_8125D_1}, +- [RTL_GIGA_MAC_VER_65] = {"RTL8125D", FIRMWARE_8125D_2}, +- [RTL_GIGA_MAC_VER_66] = {"RTL8125BP", FIRMWARE_8125BP_2}, +- [RTL_GIGA_MAC_VER_70] = {"RTL8126A", FIRMWARE_8126A_2}, +- [RTL_GIGA_MAC_VER_71] = {"RTL8126A", FIRMWARE_8126A_3}, ++ /* 8126A family. */ ++ { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_71, "RTL8126A", FIRMWARE_8126A_3 }, ++ { 0x7cf, 0x649, RTL_GIGA_MAC_VER_70, "RTL8126A", FIRMWARE_8126A_2 }, ++ ++ /* 8125BP family. */ ++ { 0x7cf, 0x681, RTL_GIGA_MAC_VER_66, "RTL8125BP", FIRMWARE_8125BP_2 }, ++ ++ /* 8125D family. */ ++ { 0x7cf, 0x689, RTL_GIGA_MAC_VER_65, "RTL8125D", FIRMWARE_8125D_2 }, ++ { 0x7cf, 0x688, RTL_GIGA_MAC_VER_64, "RTL8125D", FIRMWARE_8125D_1 }, ++ ++ /* 8125B family. */ ++ { 0x7cf, 0x641, RTL_GIGA_MAC_VER_63, "RTL8125B", FIRMWARE_8125B_2 }, ++ ++ /* 8125A family. */ ++ { 0x7cf, 0x609, RTL_GIGA_MAC_VER_61, "RTL8125A", FIRMWARE_8125A_3 }, ++ ++ /* RTL8117 */ ++ { 0x7cf, 0x54b, RTL_GIGA_MAC_VER_53, "RTL8168fp/RTL8117" }, ++ { 0x7cf, 0x54a, RTL_GIGA_MAC_VER_52, "RTL8168fp/RTL8117", ++ FIRMWARE_8168FP_3 }, ++ ++ /* 8168EP family. */ ++ { 0x7cf, 0x502, RTL_GIGA_MAC_VER_51, "RTL8168ep/8111ep" }, ++ ++ /* 8168H family. */ ++ { 0x7cf, 0x541, RTL_GIGA_MAC_VER_46, "RTL8168h/8111h", ++ FIRMWARE_8168H_2 }, ++ /* Realtek calls it RTL8168M, but it's handled like RTL8168H */ ++ { 0x7cf, 0x6c0, RTL_GIGA_MAC_VER_46, "RTL8168M", FIRMWARE_8168H_2 }, ++ ++ /* 8168G family. */ ++ { 0x7cf, 0x5c8, RTL_GIGA_MAC_VER_44, "RTL8411b", FIRMWARE_8411_2 }, ++ { 0x7cf, 0x509, RTL_GIGA_MAC_VER_42, "RTL8168gu/8111gu", ++ FIRMWARE_8168G_3 }, ++ { 0x7cf, 0x4c0, RTL_GIGA_MAC_VER_40, "RTL8168g/8111g", ++ FIRMWARE_8168G_2 }, ++ ++ /* 8168F family. */ ++ { 0x7c8, 0x488, RTL_GIGA_MAC_VER_38, "RTL8411", FIRMWARE_8411_1 }, ++ { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36, "RTL8168f/8111f", ++ FIRMWARE_8168F_2 }, ++ { 0x7cf, 0x480, RTL_GIGA_MAC_VER_35, "RTL8168f/8111f", ++ FIRMWARE_8168F_1 }, ++ ++ /* 8168E family. */ ++ { 0x7c8, 0x2c8, RTL_GIGA_MAC_VER_34, "RTL8168evl/8111evl", ++ FIRMWARE_8168E_3 }, ++ { 0x7cf, 0x2c1, RTL_GIGA_MAC_VER_32, "RTL8168e/8111e", ++ FIRMWARE_8168E_1 }, ++ { 0x7c8, 0x2c0, RTL_GIGA_MAC_VER_33, "RTL8168e/8111e", ++ FIRMWARE_8168E_2 }, ++ ++ /* 8168D family. */ ++ { 0x7cf, 0x281, RTL_GIGA_MAC_VER_25, "RTL8168d/8111d", ++ FIRMWARE_8168D_1 }, ++ { 0x7c8, 0x280, RTL_GIGA_MAC_VER_26, "RTL8168d/8111d", ++ FIRMWARE_8168D_2 }, ++ ++ /* 8168DP family. */ ++ { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28, "RTL8168dp/8111dp" }, ++ { 0x7cf, 0x28b, RTL_GIGA_MAC_VER_31, "RTL8168dp/8111dp" }, ++ ++ /* 8168C family. */ ++ { 0x7cf, 0x3c9, RTL_GIGA_MAC_VER_23, "RTL8168cp/8111cp" }, ++ { 0x7cf, 0x3c8, RTL_GIGA_MAC_VER_18, "RTL8168cp/8111cp" }, ++ { 0x7c8, 0x3c8, RTL_GIGA_MAC_VER_24, "RTL8168cp/8111cp" }, ++ { 0x7cf, 0x3c0, RTL_GIGA_MAC_VER_19, "RTL8168c/8111c" }, ++ { 0x7cf, 0x3c2, RTL_GIGA_MAC_VER_20, "RTL8168c/8111c" }, ++ { 0x7cf, 0x3c3, RTL_GIGA_MAC_VER_21, "RTL8168c/8111c" }, ++ { 0x7c8, 0x3c0, RTL_GIGA_MAC_VER_22, "RTL8168c/8111c" }, ++ ++ /* 8168B family. */ ++ { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17, "RTL8168b/8111b" }, ++ /* This one is very old and rare, support has been removed. ++ * { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11, "RTL8168b/8111b" }, ++ */ ++ ++ /* 8101 family. */ ++ { 0x7c8, 0x448, RTL_GIGA_MAC_VER_39, "RTL8106e", FIRMWARE_8106E_1 }, ++ { 0x7c8, 0x440, RTL_GIGA_MAC_VER_37, "RTL8402", FIRMWARE_8402_1 }, ++ { 0x7cf, 0x409, RTL_GIGA_MAC_VER_29, "RTL8105e", FIRMWARE_8105E_1 }, ++ { 0x7c8, 0x408, RTL_GIGA_MAC_VER_30, "RTL8105e", FIRMWARE_8105E_1 }, ++ { 0x7cf, 0x349, RTL_GIGA_MAC_VER_08, "RTL8102e" }, ++ { 0x7cf, 0x249, RTL_GIGA_MAC_VER_08, "RTL8102e" }, ++ { 0x7cf, 0x348, RTL_GIGA_MAC_VER_07, "RTL8102e" }, ++ { 0x7cf, 0x248, RTL_GIGA_MAC_VER_07, "RTL8102e" }, ++ { 0x7cf, 0x240, RTL_GIGA_MAC_VER_14, "RTL8401" }, ++ { 0x7c8, 0x348, RTL_GIGA_MAC_VER_09, "RTL8102e/RTL8103e" }, ++ { 0x7c8, 0x248, RTL_GIGA_MAC_VER_09, "RTL8102e/RTL8103e" }, ++ { 0x7c8, 0x340, RTL_GIGA_MAC_VER_10, "RTL8101e/RTL8100e" }, ++ ++ /* 8110 family. */ ++ { 0xfc8, 0x980, RTL_GIGA_MAC_VER_06, "RTL8169sc/8110sc" }, ++ { 0xfc8, 0x180, RTL_GIGA_MAC_VER_05, "RTL8169sc/8110sc" }, ++ { 0xfc8, 0x100, RTL_GIGA_MAC_VER_04, "RTL8169sb/8110sb" }, ++ { 0xfc8, 0x040, RTL_GIGA_MAC_VER_03, "RTL8110s" }, ++ { 0xfc8, 0x008, RTL_GIGA_MAC_VER_02, "RTL8169s" }, ++ ++ /* Catch-all */ ++ { 0x000, 0x000, RTL_GIGA_MAC_NONE } + }; + + static const struct pci_device_id rtl8169_pci_tbl[] = { +@@ -2265,151 +2318,30 @@ static const struct ethtool_ops rtl8169_ + .get_eth_ctrl_stats = rtl8169_get_eth_ctrl_stats, + }; + +-static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) ++static const struct rtl_chip_info *rtl8169_get_chip_version(u16 xid, bool gmii) + { +- /* +- * The driver currently handles the 8168Bf and the 8168Be identically +- * but they can be identified more specifically through the test below +- * if needed: +- * +- * (RTL_R32(tp, TxConfig) & 0x700000) == 0x500000 ? 8168Bf : 8168Be +- * +- * Same thing for the 8101Eb and the 8101Ec: +- * +- * (RTL_R32(tp, TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec +- */ +- static const struct rtl_mac_info { +- u16 mask; +- u16 val; +- enum mac_version ver; +- } mac_info[] = { +- /* 8126A family. */ +- { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_71 }, +- { 0x7cf, 0x649, RTL_GIGA_MAC_VER_70 }, +- +- /* 8125BP family. */ +- { 0x7cf, 0x681, RTL_GIGA_MAC_VER_66 }, +- +- /* 8125D family. */ +- { 0x7cf, 0x689, RTL_GIGA_MAC_VER_65 }, +- { 0x7cf, 0x688, RTL_GIGA_MAC_VER_64 }, +- +- /* 8125B family. */ +- { 0x7cf, 0x641, RTL_GIGA_MAC_VER_63 }, +- +- /* 8125A family. */ +- { 0x7cf, 0x609, RTL_GIGA_MAC_VER_61 }, +- /* It seems only XID 609 made it to the mass market. +- * { 0x7cf, 0x608, RTL_GIGA_MAC_VER_60 }, +- * { 0x7c8, 0x608, RTL_GIGA_MAC_VER_61 }, +- */ +- +- /* RTL8117 */ +- { 0x7cf, 0x54b, RTL_GIGA_MAC_VER_53 }, +- { 0x7cf, 0x54a, RTL_GIGA_MAC_VER_52 }, +- +- /* 8168EP family. */ +- { 0x7cf, 0x502, RTL_GIGA_MAC_VER_51 }, +- /* It seems this chip version never made it to +- * the wild. Let's disable detection. +- * { 0x7cf, 0x501, RTL_GIGA_MAC_VER_50 }, +- * { 0x7cf, 0x500, RTL_GIGA_MAC_VER_49 }, +- */ +- +- /* 8168H family. */ +- { 0x7cf, 0x541, RTL_GIGA_MAC_VER_46 }, +- /* It seems this chip version never made it to +- * the wild. Let's disable detection. +- * { 0x7cf, 0x540, RTL_GIGA_MAC_VER_45 }, +- */ +- /* Realtek calls it RTL8168M, but it's handled like RTL8168H */ +- { 0x7cf, 0x6c0, RTL_GIGA_MAC_VER_46 }, +- +- /* 8168G family. */ +- { 0x7cf, 0x5c8, RTL_GIGA_MAC_VER_44 }, +- { 0x7cf, 0x509, RTL_GIGA_MAC_VER_42 }, +- /* It seems this chip version never made it to +- * the wild. Let's disable detection. +- * { 0x7cf, 0x4c1, RTL_GIGA_MAC_VER_41 }, +- */ +- { 0x7cf, 0x4c0, RTL_GIGA_MAC_VER_40 }, +- +- /* 8168F family. */ +- { 0x7c8, 0x488, RTL_GIGA_MAC_VER_38 }, +- { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36 }, +- { 0x7cf, 0x480, RTL_GIGA_MAC_VER_35 }, +- +- /* 8168E family. */ +- { 0x7c8, 0x2c8, RTL_GIGA_MAC_VER_34 }, +- { 0x7cf, 0x2c1, RTL_GIGA_MAC_VER_32 }, +- { 0x7c8, 0x2c0, RTL_GIGA_MAC_VER_33 }, +- +- /* 8168D family. */ +- { 0x7cf, 0x281, RTL_GIGA_MAC_VER_25 }, +- { 0x7c8, 0x280, RTL_GIGA_MAC_VER_26 }, +- +- /* 8168DP family. */ +- /* It seems this early RTL8168dp version never made it to +- * the wild. Support has been removed. +- * { 0x7cf, 0x288, RTL_GIGA_MAC_VER_27 }, +- */ +- { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28 }, +- { 0x7cf, 0x28b, RTL_GIGA_MAC_VER_31 }, +- +- /* 8168C family. */ +- { 0x7cf, 0x3c9, RTL_GIGA_MAC_VER_23 }, +- { 0x7cf, 0x3c8, RTL_GIGA_MAC_VER_18 }, +- { 0x7c8, 0x3c8, RTL_GIGA_MAC_VER_24 }, +- { 0x7cf, 0x3c0, RTL_GIGA_MAC_VER_19 }, +- { 0x7cf, 0x3c2, RTL_GIGA_MAC_VER_20 }, +- { 0x7cf, 0x3c3, RTL_GIGA_MAC_VER_21 }, +- { 0x7c8, 0x3c0, RTL_GIGA_MAC_VER_22 }, +- +- /* 8168B family. */ +- { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17 }, +- /* This one is very old and rare, support has been removed. +- * { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 }, +- */ +- +- /* 8101 family. */ +- { 0x7c8, 0x448, RTL_GIGA_MAC_VER_39 }, +- { 0x7c8, 0x440, RTL_GIGA_MAC_VER_37 }, +- { 0x7cf, 0x409, RTL_GIGA_MAC_VER_29 }, +- { 0x7c8, 0x408, RTL_GIGA_MAC_VER_30 }, +- { 0x7cf, 0x349, RTL_GIGA_MAC_VER_08 }, +- { 0x7cf, 0x249, RTL_GIGA_MAC_VER_08 }, +- { 0x7cf, 0x348, RTL_GIGA_MAC_VER_07 }, +- { 0x7cf, 0x248, RTL_GIGA_MAC_VER_07 }, +- { 0x7cf, 0x240, RTL_GIGA_MAC_VER_14 }, +- { 0x7c8, 0x348, RTL_GIGA_MAC_VER_09 }, +- { 0x7c8, 0x248, RTL_GIGA_MAC_VER_09 }, +- { 0x7c8, 0x340, RTL_GIGA_MAC_VER_10 }, +- +- /* 8110 family. */ +- { 0xfc8, 0x980, RTL_GIGA_MAC_VER_06 }, +- { 0xfc8, 0x180, RTL_GIGA_MAC_VER_05 }, +- { 0xfc8, 0x100, RTL_GIGA_MAC_VER_04 }, +- { 0xfc8, 0x040, RTL_GIGA_MAC_VER_03 }, +- { 0xfc8, 0x008, RTL_GIGA_MAC_VER_02 }, +- +- /* Catch-all */ +- { 0x000, 0x000, RTL_GIGA_MAC_NONE } ++ /* Chips combining a 1Gbps MAC with a 100Mbps PHY */ ++ static const struct rtl_chip_info rtl8106eus_info = { ++ .mac_version = RTL_GIGA_MAC_VER_43, ++ .name = "RTL8106eus", ++ .fw_name = FIRMWARE_8106E_2, + }; +- const struct rtl_mac_info *p = mac_info; +- enum mac_version ver; ++ static const struct rtl_chip_info rtl8107e_info = { ++ .mac_version = RTL_GIGA_MAC_VER_48, ++ .name = "RTL8107e", ++ .fw_name = FIRMWARE_8107E_2, ++ }; ++ const struct rtl_chip_info *p = rtl_chip_infos; + + while ((xid & p->mask) != p->val) + p++; +- ver = p->ver; + +- if (ver != RTL_GIGA_MAC_NONE && !gmii) { +- if (ver == RTL_GIGA_MAC_VER_42) +- ver = RTL_GIGA_MAC_VER_43; +- else if (ver == RTL_GIGA_MAC_VER_46) +- ver = RTL_GIGA_MAC_VER_48; +- } ++ if (p->mac_version == RTL_GIGA_MAC_VER_42 && !gmii) ++ return &rtl8106eus_info; ++ if (p->mac_version == RTL_GIGA_MAC_VER_46 && !gmii) ++ return &rtl8107e_info; + +- return ver; ++ return p; + } + + static void rtl_release_firmware(struct rtl8169_private *tp) +@@ -5437,9 +5369,9 @@ static bool rtl_aspm_is_safe(struct rtl8 + + static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) + { ++ const struct rtl_chip_info *chip; + struct rtl8169_private *tp; + int jumbo_max, region, rc; +- enum mac_version chipset; + struct net_device *dev; + u32 txconfig; + u16 xid; +@@ -5489,12 +5421,13 @@ static int rtl_init_one(struct pci_dev * + xid = (txconfig >> 20) & 0xfcf; + + /* Identify chip attached to board */ +- chipset = rtl8169_get_mac_version(xid, tp->supports_gmii); +- if (chipset == RTL_GIGA_MAC_NONE) ++ chip = rtl8169_get_chip_version(xid, tp->supports_gmii); ++ if (chip->mac_version == RTL_GIGA_MAC_NONE) + return dev_err_probe(&pdev->dev, -ENODEV, + "unknown chip XID %03x, contact r8169 maintainers (see MAINTAINERS file)\n", + xid); +- tp->mac_version = chipset; ++ tp->mac_version = chip->mac_version; ++ tp->fw_name = chip->fw_name; + + /* Disable ASPM L1 as that cause random device stop working + * problems as well as full system hangs for some PCIe devices users. +@@ -5599,8 +5532,6 @@ static int rtl_init_one(struct pci_dev * + + rtl_set_irq_mask(tp); + +- tp->fw_name = rtl_chip_infos[chipset].fw_name; +- + tp->counters = dmam_alloc_coherent (&pdev->dev, sizeof(*tp->counters), + &tp->counters_phys_addr, + GFP_KERNEL); +@@ -5625,7 +5556,7 @@ static int rtl_init_one(struct pci_dev * + } + + netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n", +- rtl_chip_infos[chipset].name, dev->dev_addr, xid, tp->irq); ++ chip->name, dev->dev_addr, xid, tp->irq); + + if (jumbo_max) + netdev_info(dev, "jumbo features [frames: %d bytes, tx checksumming: %s]\n", diff --git a/target/linux/generic/backport-6.12/780-37-v6.16-r8169-add-RTL_GIGA_MAC_VER_LAST-to-facilitate-adding.patch b/target/linux/generic/backport-6.12/780-37-v6.16-r8169-add-RTL_GIGA_MAC_VER_LAST-to-facilitate-adding.patch new file mode 100644 index 00000000000..6dad3f38e1e --- /dev/null +++ b/target/linux/generic/backport-6.12/780-37-v6.16-r8169-add-RTL_GIGA_MAC_VER_LAST-to-facilitate-adding.patch @@ -0,0 +1,159 @@ +From fe733618b27a8c033f0d246c2efff56fca322656 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Tue, 15 Apr 2025 21:39:23 +0200 +Subject: [PATCH] r8169: add RTL_GIGA_MAC_VER_LAST to facilitate adding support + for new chip versions + +Add a new mac_version enum value RTL_GIGA_MAC_VER_LAST. Benefit is that +when adding support for a new chip version we have to touch less code, +except something changes fundamentally. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/06991f47-2aec-4aa2-8918-2c6e79332303@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169.h | 3 ++- + drivers/net/ethernet/realtek/r8169_main.c | 28 +++++++++++------------ + 2 files changed, 16 insertions(+), 15 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169.h ++++ b/drivers/net/ethernet/realtek/r8169.h +@@ -73,7 +73,8 @@ enum mac_version { + RTL_GIGA_MAC_VER_66, + RTL_GIGA_MAC_VER_70, + RTL_GIGA_MAC_VER_71, +- RTL_GIGA_MAC_NONE ++ RTL_GIGA_MAC_NONE, ++ RTL_GIGA_MAC_VER_LAST = RTL_GIGA_MAC_NONE - 1 + }; + + struct rtl8169_private; +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -1289,7 +1289,7 @@ static void rtl_writephy(struct rtl8169_ + case RTL_GIGA_MAC_VER_31: + r8168dp_2_mdio_write(tp, location, val); + break; +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: + r8168g_mdio_write(tp, location, val); + break; + default: +@@ -1304,7 +1304,7 @@ static int rtl_readphy(struct rtl8169_pr + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + return r8168dp_2_mdio_read(tp, location); +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: + return r8168g_mdio_read(tp, location); + default: + return r8169_mdio_read(tp, location); +@@ -1656,7 +1656,7 @@ static void __rtl8169_set_wol(struct rtl + break; + case RTL_GIGA_MAC_VER_34: + case RTL_GIGA_MAC_VER_37: +- case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_LAST: + r8169_mod_reg8_cond(tp, Config2, PME_SIGNAL, wolopts); + break; + default: +@@ -2129,7 +2129,7 @@ static void rtl_set_eee_txidle_timer(str + tp->tx_lpi_timer = timer_val; + r8168_mac_ocp_write(tp, 0xe048, timer_val); + break; +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + tp->tx_lpi_timer = timer_val; + RTL_W16(tp, EEE_TXIDLE_TIMER_8125, timer_val); + break; +@@ -2491,7 +2491,7 @@ static void rtl_init_rxcfg(struct rtl816 + case RTL_GIGA_MAC_VER_61: + RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST); + break; +- case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_LAST: + RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST | + RX_PAUSE_SLOT_ON); + break; +@@ -2623,7 +2623,7 @@ static void rtl_wait_txrx_fifo_empty(str + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61: + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + break; +- case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_LAST: + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond_2, 100, 42); +@@ -2895,7 +2895,7 @@ static void rtl_enable_exit_l1(struct rt + case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38: + rtl_eri_set_bits(tp, 0xd4, 0x0c00); + break; +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: + r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80); + break; + default: +@@ -2909,7 +2909,7 @@ static void rtl_disable_exit_l1(struct r + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38: + rtl_eri_clear_bits(tp, 0xd4, 0x1f00); + break; +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: + r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0); + break; + default: +@@ -2947,7 +2947,7 @@ static void rtl_hw_aspm_clkreq_enable(st + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + /* reset ephy tx/rx disable timer */ + r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0); + /* chip can trigger L1.2 */ +@@ -2959,7 +2959,7 @@ static void rtl_hw_aspm_clkreq_enable(st + } else { + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0); + break; + default: +@@ -4091,7 +4091,7 @@ static void rtl8169_cleanup(struct rtl81 + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); + rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666); + break; +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: + rtl_enable_rxdvgate(tp); + fsleep(2000); + break; +@@ -4248,7 +4248,7 @@ static unsigned int rtl_quirk_packet_pad + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_34: +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + padto = max_t(unsigned int, padto, ETH_ZLEN); + break; + default: +@@ -5299,7 +5299,7 @@ static void rtl_hw_initialize(struct rtl + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48: + rtl_hw_init_8168g(tp); + break; +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + rtl_hw_init_8125(tp); + break; + default: +@@ -5324,7 +5324,7 @@ static int rtl_jumbo_max(struct rtl8169_ + case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_24: + return JUMBO_6K; + /* RTL8125/8126 */ +- case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_71: ++ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + return JUMBO_16K; + default: + return JUMBO_9K; diff --git a/target/linux/generic/backport-6.12/780-38-v6.16-r8169-use-pci_prepare_to_sleep-in-rtl_shutdown.patch b/target/linux/generic/backport-6.12/780-38-v6.16-r8169-use-pci_prepare_to_sleep-in-rtl_shutdown.patch new file mode 100644 index 00000000000..0f085f08e0f --- /dev/null +++ b/target/linux/generic/backport-6.12/780-38-v6.16-r8169-use-pci_prepare_to_sleep-in-rtl_shutdown.patch @@ -0,0 +1,37 @@ +From b7ed5d5a78fccee96cf8919ac2c7a064c2f4c45b Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Mon, 21 Apr 2025 11:25:18 +0200 +Subject: [PATCH] r8169: use pci_prepare_to_sleep in rtl_shutdown + +Use pci_prepare_to_sleep() like PCI core does in pci_pm_suspend_noirq. +This aligns setting a low-power mode during shutdown with the handling +of the transition to system suspend. Also the transition to runtime +suspend uses pci_target_state() instead of setting D3hot unconditionally. + +Note: pci_prepare_to_sleep() uses device_may_wakeup() to check whether + device may generate wakeup events. So we don't lose anything by + not passing tp->saved_wolopts any longer. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Jacob Keller +Link: https://patch.msgid.link/f573fdbd-ba6d-41c1-b68f-311d3c88db2c@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -5037,10 +5037,8 @@ static void rtl_shutdown(struct pci_dev + /* Restore original MAC address */ + rtl_rar_set(tp, tp->dev->perm_addr); + +- if (system_state == SYSTEM_POWER_OFF && !tp->dash_enabled) { +- pci_wake_from_d3(pdev, tp->saved_wolopts); +- pci_set_power_state(pdev, PCI_D3hot); +- } ++ if (system_state == SYSTEM_POWER_OFF && !tp->dash_enabled) ++ pci_prepare_to_sleep(pdev); + } + + static void rtl_remove_one(struct pci_dev *pdev) diff --git a/target/linux/generic/backport-6.12/780-39-v6.16-r8169-merge-chip-versions-70-and-71-RTL8126A.patch b/target/linux/generic/backport-6.12/780-39-v6.16-r8169-merge-chip-versions-70-and-71-RTL8126A.patch new file mode 100644 index 00000000000..caab6c69f10 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-39-v6.16-r8169-merge-chip-versions-70-and-71-RTL8126A.patch @@ -0,0 +1,106 @@ +From 4dec0702b8627c364a0e1f2c5b249e06709a1c24 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Fri, 18 Apr 2025 11:23:45 +0200 +Subject: [PATCH] r8169: merge chip versions 70 and 71 (RTL8126A) + +Handling of both chip versions is the same, only difference is +the firmware. So we can merge handling of both chip versions. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/97d7ae79-d021-4b6b-b424-89e5e305b029@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169.h | 1 - + drivers/net/ethernet/realtek/r8169_main.c | 15 ++++----------- + drivers/net/ethernet/realtek/r8169_phy_config.c | 1 - + 3 files changed, 4 insertions(+), 13 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169.h ++++ b/drivers/net/ethernet/realtek/r8169.h +@@ -72,7 +72,6 @@ enum mac_version { + RTL_GIGA_MAC_VER_65, + RTL_GIGA_MAC_VER_66, + RTL_GIGA_MAC_VER_70, +- RTL_GIGA_MAC_VER_71, + RTL_GIGA_MAC_NONE, + RTL_GIGA_MAC_VER_LAST = RTL_GIGA_MAC_NONE - 1 + }; +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -99,7 +99,7 @@ static const struct rtl_chip_info { + const char *fw_name; + } rtl_chip_infos[] = { + /* 8126A family. */ +- { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_71, "RTL8126A", FIRMWARE_8126A_3 }, ++ { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_70, "RTL8126A", FIRMWARE_8126A_3 }, + { 0x7cf, 0x649, RTL_GIGA_MAC_VER_70, "RTL8126A", FIRMWARE_8126A_2 }, + + /* 8125BP family. */ +@@ -2936,7 +2936,6 @@ static void rtl_hw_aspm_clkreq_enable(st + rtl_mod_config5(tp, 0, ASPM_en); + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_70: +- case RTL_GIGA_MAC_VER_71: + val8 = RTL_R8(tp, INT_CFG0_8125) | INT_CFG0_CLKREQEN; + RTL_W8(tp, INT_CFG0_8125, val8); + break; +@@ -2968,7 +2967,6 @@ static void rtl_hw_aspm_clkreq_enable(st + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_70: +- case RTL_GIGA_MAC_VER_71: + val8 = RTL_R8(tp, INT_CFG0_8125) & ~INT_CFG0_CLKREQEN; + RTL_W8(tp, INT_CFG0_8125, val8); + break; +@@ -3688,12 +3686,10 @@ static void rtl_hw_start_8125_common(str + /* disable new tx descriptor format */ + r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000); + +- if (tp->mac_version == RTL_GIGA_MAC_VER_70 || +- tp->mac_version == RTL_GIGA_MAC_VER_71) ++ if (tp->mac_version == RTL_GIGA_MAC_VER_70) + RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02); + +- if (tp->mac_version == RTL_GIGA_MAC_VER_70 || +- tp->mac_version == RTL_GIGA_MAC_VER_71) ++ if (tp->mac_version == RTL_GIGA_MAC_VER_70) + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400); + else if (tp->mac_version == RTL_GIGA_MAC_VER_63) + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200); +@@ -3711,8 +3707,7 @@ static void rtl_hw_start_8125_common(str + r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0000); + r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000); + r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001); +- if (tp->mac_version == RTL_GIGA_MAC_VER_70 || +- tp->mac_version == RTL_GIGA_MAC_VER_71) ++ if (tp->mac_version == RTL_GIGA_MAC_VER_70) + r8168_mac_ocp_modify(tp, 0xea1c, 0x0300, 0x0000); + else + r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000); +@@ -3835,7 +3830,6 @@ static void rtl_hw_config(struct rtl8169 + [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_66] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_70] = rtl_hw_start_8126a, +- [RTL_GIGA_MAC_VER_71] = rtl_hw_start_8126a, + }; + + if (hw_configs[tp->mac_version]) +@@ -3859,7 +3853,6 @@ static void rtl_hw_start_8125(struct rtl + break; + case RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_70: +- case RTL_GIGA_MAC_VER_71: + for (i = 0xa00; i < 0xa80; i += 4) + RTL_W32(tp, i, 0); + RTL_W16(tp, INT_CFG1_8125, 0x0000); +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -1183,7 +1183,6 @@ void r8169_hw_phy_config(struct rtl8169_ + [RTL_GIGA_MAC_VER_65] = rtl8125d_hw_phy_config, + [RTL_GIGA_MAC_VER_66] = rtl8125bp_hw_phy_config, + [RTL_GIGA_MAC_VER_70] = rtl8126a_hw_phy_config, +- [RTL_GIGA_MAC_VER_71] = rtl8126a_hw_phy_config, + }; + + if (phy_configs[ver]) diff --git a/target/linux/generic/backport-6.12/780-40-v6.16-r8169-merge-chip-versions-64-and-65-RTL8125D.patch b/target/linux/generic/backport-6.12/780-40-v6.16-r8169-merge-chip-versions-64-and-65-RTL8125D.patch new file mode 100644 index 00000000000..5110c2776e4 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-40-v6.16-r8169-merge-chip-versions-64-and-65-RTL8125D.patch @@ -0,0 +1,65 @@ +From f372ef6ed5a6b0401c884561d4bba1843e54d46a Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Fri, 18 Apr 2025 11:24:30 +0200 +Subject: [PATCH] r8169: merge chip versions 64 and 65 (RTL8125D) + +Handling of both chip versions is the same, only difference is +the firmware. So we can merge handling of both chip versions. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/0baad123-c679-4154-923f-fdc12783e900@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169.h | 1 - + drivers/net/ethernet/realtek/r8169_main.c | 4 +--- + drivers/net/ethernet/realtek/r8169_phy_config.c | 1 - + 3 files changed, 1 insertion(+), 5 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169.h ++++ b/drivers/net/ethernet/realtek/r8169.h +@@ -69,7 +69,6 @@ enum mac_version { + RTL_GIGA_MAC_VER_61, + RTL_GIGA_MAC_VER_63, + RTL_GIGA_MAC_VER_64, +- RTL_GIGA_MAC_VER_65, + RTL_GIGA_MAC_VER_66, + RTL_GIGA_MAC_VER_70, + RTL_GIGA_MAC_NONE, +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -106,7 +106,7 @@ static const struct rtl_chip_info { + { 0x7cf, 0x681, RTL_GIGA_MAC_VER_66, "RTL8125BP", FIRMWARE_8125BP_2 }, + + /* 8125D family. */ +- { 0x7cf, 0x689, RTL_GIGA_MAC_VER_65, "RTL8125D", FIRMWARE_8125D_2 }, ++ { 0x7cf, 0x689, RTL_GIGA_MAC_VER_64, "RTL8125D", FIRMWARE_8125D_2 }, + { 0x7cf, 0x688, RTL_GIGA_MAC_VER_64, "RTL8125D", FIRMWARE_8125D_1 }, + + /* 8125B family. */ +@@ -3827,7 +3827,6 @@ static void rtl_hw_config(struct rtl8169 + [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2, + [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b, + [RTL_GIGA_MAC_VER_64] = rtl_hw_start_8125d, +- [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_66] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_70] = rtl_hw_start_8126a, + }; +@@ -3846,7 +3845,6 @@ static void rtl_hw_start_8125(struct rtl + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_61: + case RTL_GIGA_MAC_VER_64: +- case RTL_GIGA_MAC_VER_65: + case RTL_GIGA_MAC_VER_66: + for (i = 0xa00; i < 0xb00; i += 4) + RTL_W32(tp, i, 0); +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -1180,7 +1180,6 @@ void r8169_hw_phy_config(struct rtl8169_ + [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config, + [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config, + [RTL_GIGA_MAC_VER_64] = rtl8125d_hw_phy_config, +- [RTL_GIGA_MAC_VER_65] = rtl8125d_hw_phy_config, + [RTL_GIGA_MAC_VER_66] = rtl8125bp_hw_phy_config, + [RTL_GIGA_MAC_VER_70] = rtl8126a_hw_phy_config, + }; diff --git a/target/linux/generic/backport-6.12/780-41-v6.16-r8169-merge-chip-versions-52-and-53-RTL8117.patch b/target/linux/generic/backport-6.12/780-41-v6.16-r8169-merge-chip-versions-52-and-53-RTL8117.patch new file mode 100644 index 00000000000..b28c28f4e26 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-41-v6.16-r8169-merge-chip-versions-52-and-53-RTL8117.patch @@ -0,0 +1,113 @@ +From 4f51e7d370a04122fa78470b031d6487c52298b1 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Fri, 18 Apr 2025 11:25:17 +0200 +Subject: [PATCH] r8169: merge chip versions 52 and 53 (RTL8117) + +Handling of both chip versions is the same, only difference is +the firmware. So we can merge handling of both chip versions. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/ae866b71-c904-434e-befb-848c831e33ff@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169.h | 1 - + drivers/net/ethernet/realtek/r8169_main.c | 17 +++++++---------- + drivers/net/ethernet/realtek/r8169_phy_config.c | 1 - + 3 files changed, 7 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169.h ++++ b/drivers/net/ethernet/realtek/r8169.h +@@ -64,7 +64,6 @@ enum mac_version { + /* support for RTL_GIGA_MAC_VER_50 has been removed */ + RTL_GIGA_MAC_VER_51, + RTL_GIGA_MAC_VER_52, +- RTL_GIGA_MAC_VER_53, + /* support for RTL_GIGA_MAC_VER_60 has been removed */ + RTL_GIGA_MAC_VER_61, + RTL_GIGA_MAC_VER_63, +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -116,7 +116,7 @@ static const struct rtl_chip_info { + { 0x7cf, 0x609, RTL_GIGA_MAC_VER_61, "RTL8125A", FIRMWARE_8125A_3 }, + + /* RTL8117 */ +- { 0x7cf, 0x54b, RTL_GIGA_MAC_VER_53, "RTL8168fp/RTL8117" }, ++ { 0x7cf, 0x54b, RTL_GIGA_MAC_VER_52, "RTL8168fp/RTL8117" }, + { 0x7cf, 0x54a, RTL_GIGA_MAC_VER_52, "RTL8168fp/RTL8117", + FIRMWARE_8168FP_3 }, + +@@ -830,7 +830,7 @@ static bool rtl_is_8168evl_up(struct rtl + { + return tp->mac_version >= RTL_GIGA_MAC_VER_34 && + tp->mac_version != RTL_GIGA_MAC_VER_39 && +- tp->mac_version <= RTL_GIGA_MAC_VER_53; ++ tp->mac_version <= RTL_GIGA_MAC_VER_52; + } + + static bool rtl_supports_eee(struct rtl8169_private *tp) +@@ -998,9 +998,7 @@ void r8169_get_led_name(struct rtl8169_p + static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type) + { + /* based on RTL8168FP_OOBMAC_BASE in vendor driver */ +- if (type == ERIAR_OOB && +- (tp->mac_version == RTL_GIGA_MAC_VER_52 || +- tp->mac_version == RTL_GIGA_MAC_VER_53)) ++ if (type == ERIAR_OOB && tp->mac_version == RTL_GIGA_MAC_VER_52) + *cmd |= 0xf70 << 18; + } + +@@ -1500,7 +1498,7 @@ static enum rtl_dash_type rtl_get_dash_t + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + return RTL_DASH_DP; +- case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53: ++ case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_52: + return RTL_DASH_EP; + case RTL_GIGA_MAC_VER_66: + return RTL_DASH_25_BP; +@@ -2485,7 +2483,7 @@ static void rtl_init_rxcfg(struct rtl816 + case RTL_GIGA_MAC_VER_38: + RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST); + break; +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_53: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_52: + RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST | RX_EARLY_OFF); + break; + case RTL_GIGA_MAC_VER_61: +@@ -2616,7 +2614,7 @@ DECLARE_RTL_COND(rtl_rxtx_empty_cond_2) + static void rtl_wait_txrx_fifo_empty(struct rtl8169_private *tp) + { + switch (tp->mac_version) { +- case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_53: ++ case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_52: + rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 42); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + break; +@@ -3823,7 +3821,6 @@ static void rtl_hw_config(struct rtl8169 + [RTL_GIGA_MAC_VER_48] = rtl_hw_start_8168h_1, + [RTL_GIGA_MAC_VER_51] = rtl_hw_start_8168ep_3, + [RTL_GIGA_MAC_VER_52] = rtl_hw_start_8117, +- [RTL_GIGA_MAC_VER_53] = rtl_hw_start_8117, + [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2, + [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b, + [RTL_GIGA_MAC_VER_64] = rtl_hw_start_8125d, +@@ -5282,7 +5279,7 @@ static void rtl_hw_init_8125(struct rtl8 + static void rtl_hw_initialize(struct rtl8169_private *tp) + { + switch (tp->mac_version) { +- case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53: ++ case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_52: + rtl8168ep_stop_cmac(tp); + fallthrough; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48: +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -1176,7 +1176,6 @@ void r8169_hw_phy_config(struct rtl8169_ + [RTL_GIGA_MAC_VER_48] = rtl8168h_2_hw_phy_config, + [RTL_GIGA_MAC_VER_51] = rtl8168ep_2_hw_phy_config, + [RTL_GIGA_MAC_VER_52] = rtl8117_hw_phy_config, +- [RTL_GIGA_MAC_VER_53] = rtl8117_hw_phy_config, + [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config, + [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config, + [RTL_GIGA_MAC_VER_64] = rtl8125d_hw_phy_config, diff --git a/target/linux/generic/backport-6.12/780-42-v6.16-r8169-add-support-for-RTL8127A.patch b/target/linux/generic/backport-6.12/780-42-v6.16-r8169-add-support-for-RTL8127A.patch new file mode 100644 index 00000000000..45587a39b78 --- /dev/null +++ b/target/linux/generic/backport-6.12/780-42-v6.16-r8169-add-support-for-RTL8127A.patch @@ -0,0 +1,323 @@ +From f24f7b2f3af9e008ded20f804d7829ee2efd43f2 Mon Sep 17 00:00:00 2001 +From: ChunHao Lin +Date: Thu, 15 May 2025 17:53:03 +0800 +Subject: [PATCH] r8169: add support for RTL8127A + +This adds support for 10Gbs chip RTL8127A. + +Signed-off-by: ChunHao Lin +Reviewed-by: Heiner Kallweit +Link: https://patch.msgid.link/20250515095303.3138-1-hau@realtek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169.h | 1 + + drivers/net/ethernet/realtek/r8169_main.c | 29 ++- + .../net/ethernet/realtek/r8169_phy_config.c | 166 ++++++++++++++++++ + 3 files changed, 193 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169.h ++++ b/drivers/net/ethernet/realtek/r8169.h +@@ -70,6 +70,7 @@ enum mac_version { + RTL_GIGA_MAC_VER_64, + RTL_GIGA_MAC_VER_66, + RTL_GIGA_MAC_VER_70, ++ RTL_GIGA_MAC_VER_80, + RTL_GIGA_MAC_NONE, + RTL_GIGA_MAC_VER_LAST = RTL_GIGA_MAC_NONE - 1 + }; +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -60,6 +60,7 @@ + #define FIRMWARE_8125BP_2 "rtl_nic/rtl8125bp-2.fw" + #define FIRMWARE_8126A_2 "rtl_nic/rtl8126a-2.fw" + #define FIRMWARE_8126A_3 "rtl_nic/rtl8126a-3.fw" ++#define FIRMWARE_8127A_1 "rtl_nic/rtl8127a-1.fw" + + #define TX_DMA_BURST 7 /* Maximum PCI burst, '7' is unlimited */ + #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ +@@ -98,6 +99,9 @@ static const struct rtl_chip_info { + const char *name; + const char *fw_name; + } rtl_chip_infos[] = { ++ /* 8127A family. */ ++ { 0x7cf, 0x6c9, RTL_GIGA_MAC_VER_80, "RTL8127A", FIRMWARE_8127A_1 }, ++ + /* 8126A family. */ + { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_70, "RTL8126A", FIRMWARE_8126A_3 }, + { 0x7cf, 0x649, RTL_GIGA_MAC_VER_70, "RTL8126A", FIRMWARE_8126A_2 }, +@@ -222,8 +226,10 @@ static const struct pci_device_id rtl816 + { 0x0001, 0x8168, PCI_ANY_ID, 0x2410 }, + { PCI_VDEVICE(REALTEK, 0x8125) }, + { PCI_VDEVICE(REALTEK, 0x8126) }, ++ { PCI_VDEVICE(REALTEK, 0x8127) }, + { PCI_VDEVICE(REALTEK, 0x3000) }, + { PCI_VDEVICE(REALTEK, 0x5000) }, ++ { PCI_VDEVICE(REALTEK, 0x0e10) }, + {} + }; + +@@ -769,6 +775,7 @@ MODULE_FIRMWARE(FIRMWARE_8125D_2); + MODULE_FIRMWARE(FIRMWARE_8125BP_2); + MODULE_FIRMWARE(FIRMWARE_8126A_2); + MODULE_FIRMWARE(FIRMWARE_8126A_3); ++MODULE_FIRMWARE(FIRMWARE_8127A_1); + + static inline struct device *tp_to_dev(struct rtl8169_private *tp) + { +@@ -2934,6 +2941,7 @@ static void rtl_hw_aspm_clkreq_enable(st + rtl_mod_config5(tp, 0, ASPM_en); + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_70: ++ case RTL_GIGA_MAC_VER_80: + val8 = RTL_R8(tp, INT_CFG0_8125) | INT_CFG0_CLKREQEN; + RTL_W8(tp, INT_CFG0_8125, val8); + break; +@@ -2965,6 +2973,7 @@ static void rtl_hw_aspm_clkreq_enable(st + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_70: ++ case RTL_GIGA_MAC_VER_80: + val8 = RTL_R8(tp, INT_CFG0_8125) & ~INT_CFG0_CLKREQEN; + RTL_W8(tp, INT_CFG0_8125, val8); + break; +@@ -3684,10 +3693,13 @@ static void rtl_hw_start_8125_common(str + /* disable new tx descriptor format */ + r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000); + +- if (tp->mac_version == RTL_GIGA_MAC_VER_70) ++ if (tp->mac_version == RTL_GIGA_MAC_VER_70 || ++ tp->mac_version == RTL_GIGA_MAC_VER_80) + RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02); + +- if (tp->mac_version == RTL_GIGA_MAC_VER_70) ++ if (tp->mac_version == RTL_GIGA_MAC_VER_80) ++ r8168_mac_ocp_modify(tp, 0xe614, 0x0f00, 0x0f00); ++ else if (tp->mac_version == RTL_GIGA_MAC_VER_70) + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400); + else if (tp->mac_version == RTL_GIGA_MAC_VER_63) + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200); +@@ -3705,7 +3717,8 @@ static void rtl_hw_start_8125_common(str + r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0000); + r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000); + r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001); +- if (tp->mac_version == RTL_GIGA_MAC_VER_70) ++ if (tp->mac_version == RTL_GIGA_MAC_VER_70 || ++ tp->mac_version == RTL_GIGA_MAC_VER_80) + r8168_mac_ocp_modify(tp, 0xea1c, 0x0300, 0x0000); + else + r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000); +@@ -3783,6 +3796,12 @@ static void rtl_hw_start_8126a(struct rt + rtl_hw_start_8125_common(tp); + } + ++static void rtl_hw_start_8127a(struct rtl8169_private *tp) ++{ ++ rtl_set_def_aspm_entry_latency(tp); ++ rtl_hw_start_8125_common(tp); ++} ++ + static void rtl_hw_config(struct rtl8169_private *tp) + { + static const rtl_generic_fct hw_configs[] = { +@@ -3826,6 +3845,7 @@ static void rtl_hw_config(struct rtl8169 + [RTL_GIGA_MAC_VER_64] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_66] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_70] = rtl_hw_start_8126a, ++ [RTL_GIGA_MAC_VER_80] = rtl_hw_start_8127a, + }; + + if (hw_configs[tp->mac_version]) +@@ -3843,8 +3863,11 @@ static void rtl_hw_start_8125(struct rtl + case RTL_GIGA_MAC_VER_61: + case RTL_GIGA_MAC_VER_64: + case RTL_GIGA_MAC_VER_66: ++ case RTL_GIGA_MAC_VER_80: + for (i = 0xa00; i < 0xb00; i += 4) + RTL_W32(tp, i, 0); ++ if (tp->mac_version == RTL_GIGA_MAC_VER_80) ++ RTL_W16(tp, INT_CFG1_8125, 0x0000); + break; + case RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_70: +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -1130,6 +1130,171 @@ static void rtl8126a_hw_phy_config(struc + rtl8125_common_config_eee_phy(phydev); + } + ++static void rtl8127a_1_hw_phy_config(struct rtl8169_private *tp, ++ struct phy_device *phydev) ++{ ++ r8169_apply_firmware(tp); ++ rtl8168g_enable_gphy_10m(phydev); ++ ++ r8168g_phy_param(phydev, 0x8415, 0xff00, 0x9300); ++ r8168g_phy_param(phydev, 0x81a3, 0xff00, 0x0f00); ++ r8168g_phy_param(phydev, 0x81ae, 0xff00, 0x0f00); ++ r8168g_phy_param(phydev, 0x81b9, 0xff00, 0xb900); ++ rtl8125_phy_param(phydev, 0x83b0, 0x0e00, 0x0000); ++ rtl8125_phy_param(phydev, 0x83C5, 0x0e00, 0x0000); ++ rtl8125_phy_param(phydev, 0x83da, 0x0e00, 0x0000); ++ rtl8125_phy_param(phydev, 0x83ef, 0x0e00, 0x0000); ++ phy_modify_paged(phydev, 0x0bf3, 0x14, 0x01f0, 0x0160); ++ phy_modify_paged(phydev, 0x0bf3, 0x15, 0x001f, 0x0014); ++ phy_modify_paged(phydev, 0x0bf2, 0x14, 0x6000, 0x0000); ++ phy_modify_paged(phydev, 0x0bf2, 0x16, 0xc000, 0x0000); ++ phy_modify_paged(phydev, 0x0bf2, 0x14, 0x1fff, 0x0187); ++ phy_modify_paged(phydev, 0x0bf2, 0x15, 0x003f, 0x0003); ++ ++ r8168g_phy_param(phydev, 0x8173, 0xffff, 0x8620); ++ r8168g_phy_param(phydev, 0x8175, 0xffff, 0x8671); ++ r8168g_phy_param(phydev, 0x817c, 0x0000, 0x2000); ++ r8168g_phy_param(phydev, 0x8187, 0x0000, 0x2000); ++ r8168g_phy_param(phydev, 0x8192, 0x0000, 0x2000); ++ r8168g_phy_param(phydev, 0x819d, 0x0000, 0x2000); ++ r8168g_phy_param(phydev, 0x81a8, 0x2000, 0x0000); ++ r8168g_phy_param(phydev, 0x81b3, 0x2000, 0x0000); ++ r8168g_phy_param(phydev, 0x81be, 0x0000, 0x2000); ++ r8168g_phy_param(phydev, 0x817d, 0xff00, 0xa600); ++ r8168g_phy_param(phydev, 0x8188, 0xff00, 0xa600); ++ r8168g_phy_param(phydev, 0x8193, 0xff00, 0xa600); ++ r8168g_phy_param(phydev, 0x819e, 0xff00, 0xa600); ++ r8168g_phy_param(phydev, 0x81a9, 0xff00, 0x1400); ++ r8168g_phy_param(phydev, 0x81b4, 0xff00, 0x1400); ++ r8168g_phy_param(phydev, 0x81bf, 0xff00, 0xa600); ++ ++ phy_modify_paged(phydev, 0x0aea, 0x15, 0x0028, 0x0000); ++ ++ rtl8125_phy_param(phydev, 0x84f0, 0xffff, 0x201c); ++ rtl8125_phy_param(phydev, 0x84f2, 0xffff, 0x3117); ++ ++ phy_write_paged(phydev, 0x0aec, 0x13, 0x0000); ++ phy_write_paged(phydev, 0x0ae2, 0x10, 0xffff); ++ phy_write_paged(phydev, 0x0aec, 0x17, 0xffff); ++ phy_write_paged(phydev, 0x0aed, 0x11, 0xffff); ++ phy_write_paged(phydev, 0x0aec, 0x14, 0x0000); ++ phy_modify_paged(phydev, 0x0aed, 0x10, 0x0001, 0x0000); ++ phy_write_paged(phydev, 0x0adb, 0x14, 0x0150); ++ rtl8125_phy_param(phydev, 0x8197, 0xff00, 0x5000); ++ rtl8125_phy_param(phydev, 0x8231, 0xff00, 0x5000); ++ rtl8125_phy_param(phydev, 0x82cb, 0xff00, 0x5000); ++ rtl8125_phy_param(phydev, 0x82cd, 0xff00, 0x5700); ++ rtl8125_phy_param(phydev, 0x8233, 0xff00, 0x5700); ++ rtl8125_phy_param(phydev, 0x8199, 0xff00, 0x5700); ++ ++ rtl8125_phy_param(phydev, 0x815a, 0xffff, 0x0150); ++ rtl8125_phy_param(phydev, 0x81f4, 0xffff, 0x0150); ++ rtl8125_phy_param(phydev, 0x828e, 0xffff, 0x0150); ++ rtl8125_phy_param(phydev, 0x81b1, 0xffff, 0x0000); ++ rtl8125_phy_param(phydev, 0x824b, 0xffff, 0x0000); ++ rtl8125_phy_param(phydev, 0x82e5, 0xffff, 0x0000); ++ ++ rtl8125_phy_param(phydev, 0x84f7, 0xff00, 0x2800); ++ phy_modify_paged(phydev, 0x0aec, 0x11, 0x0000, 0x1000); ++ rtl8125_phy_param(phydev, 0x81b3, 0xff00, 0xad00); ++ rtl8125_phy_param(phydev, 0x824d, 0xff00, 0xad00); ++ rtl8125_phy_param(phydev, 0x82e7, 0xff00, 0xad00); ++ phy_modify_paged(phydev, 0x0ae4, 0x17, 0x000f, 0x0001); ++ rtl8125_phy_param(phydev, 0x82ce, 0xf000, 0x4000); ++ ++ rtl8125_phy_param(phydev, 0x84ac, 0xffff, 0x0000); ++ rtl8125_phy_param(phydev, 0x84ae, 0xffff, 0x0000); ++ rtl8125_phy_param(phydev, 0x84b0, 0xffff, 0xf818); ++ rtl8125_phy_param(phydev, 0x84b2, 0xff00, 0x6000); ++ ++ rtl8125_phy_param(phydev, 0x8ffc, 0xffff, 0x6008); ++ rtl8125_phy_param(phydev, 0x8ffe, 0xffff, 0xf450); ++ ++ rtl8125_phy_param(phydev, 0x8015, 0x0000, 0x0200); ++ rtl8125_phy_param(phydev, 0x8016, 0x0800, 0x0000); ++ rtl8125_phy_param(phydev, 0x8fe6, 0xff00, 0x0800); ++ rtl8125_phy_param(phydev, 0x8fe4, 0xffff, 0x2114); ++ ++ rtl8125_phy_param(phydev, 0x8647, 0xffff, 0xa7b1); ++ rtl8125_phy_param(phydev, 0x8649, 0xffff, 0xbbca); ++ rtl8125_phy_param(phydev, 0x864b, 0xff00, 0xdc00); ++ ++ rtl8125_phy_param(phydev, 0x8154, 0xc000, 0x4000); ++ rtl8125_phy_param(phydev, 0x8158, 0xc000, 0x0000); ++ ++ rtl8125_phy_param(phydev, 0x826c, 0xffff, 0xffff); ++ rtl8125_phy_param(phydev, 0x826e, 0xffff, 0xffff); ++ ++ rtl8125_phy_param(phydev, 0x8872, 0xff00, 0x0e00); ++ r8168g_phy_param(phydev, 0x8012, 0x0000, 0x0800); ++ r8168g_phy_param(phydev, 0x8012, 0x0000, 0x4000); ++ phy_modify_paged(phydev, 0x0b57, 0x13, 0x0000, 0x0001); ++ r8168g_phy_param(phydev, 0x834a, 0xff00, 0x0700); ++ rtl8125_phy_param(phydev, 0x8217, 0x3f00, 0x2a00); ++ r8168g_phy_param(phydev, 0x81b1, 0xff00, 0x0b00); ++ rtl8125_phy_param(phydev, 0x8fed, 0xff00, 0x4e00); ++ ++ rtl8125_phy_param(phydev, 0x88ac, 0xff00, 0x2300); ++ phy_modify_paged(phydev, 0x0bf0, 0x16, 0x0000, 0x3800); ++ rtl8125_phy_param(phydev, 0x88de, 0xff00, 0x0000); ++ rtl8125_phy_param(phydev, 0x80b4, 0xffff, 0x5195); ++ ++ r8168g_phy_param(phydev, 0x8370, 0xffff, 0x8671); ++ r8168g_phy_param(phydev, 0x8372, 0xffff, 0x86c8); ++ ++ r8168g_phy_param(phydev, 0x8401, 0xffff, 0x86c8); ++ r8168g_phy_param(phydev, 0x8403, 0xffff, 0x86da); ++ r8168g_phy_param(phydev, 0x8406, 0x1800, 0x1000); ++ r8168g_phy_param(phydev, 0x8408, 0x1800, 0x1000); ++ r8168g_phy_param(phydev, 0x840a, 0x1800, 0x1000); ++ r8168g_phy_param(phydev, 0x840c, 0x1800, 0x1000); ++ r8168g_phy_param(phydev, 0x840e, 0x1800, 0x1000); ++ r8168g_phy_param(phydev, 0x8410, 0x1800, 0x1000); ++ r8168g_phy_param(phydev, 0x8412, 0x1800, 0x1000); ++ r8168g_phy_param(phydev, 0x8414, 0x1800, 0x1000); ++ r8168g_phy_param(phydev, 0x8416, 0x1800, 0x1000); ++ ++ r8168g_phy_param(phydev, 0x82bd, 0xffff, 0x1f40); ++ ++ phy_modify_paged(phydev, 0x0bfb, 0x12, 0x07ff, 0x0328); ++ phy_write_paged(phydev, 0x0bfb, 0x13, 0x3e14); ++ ++ r8168g_phy_param(phydev, 0x81c4, 0xffff, 0x003b); ++ r8168g_phy_param(phydev, 0x81c6, 0xffff, 0x0086); ++ r8168g_phy_param(phydev, 0x81c8, 0xffff, 0x00b7); ++ r8168g_phy_param(phydev, 0x81ca, 0xffff, 0x00db); ++ r8168g_phy_param(phydev, 0x81cc, 0xffff, 0x00fe); ++ r8168g_phy_param(phydev, 0x81ce, 0xffff, 0x00fe); ++ r8168g_phy_param(phydev, 0x81d0, 0xffff, 0x00fe); ++ r8168g_phy_param(phydev, 0x81d2, 0xffff, 0x00fe); ++ r8168g_phy_param(phydev, 0x81d4, 0xffff, 0x00c3); ++ r8168g_phy_param(phydev, 0x81d6, 0xffff, 0x0078); ++ r8168g_phy_param(phydev, 0x81d8, 0xffff, 0x0047); ++ r8168g_phy_param(phydev, 0x81da, 0xffff, 0x0023); ++ ++ rtl8125_phy_param(phydev, 0x88d7, 0xffff, 0x01a0); ++ rtl8125_phy_param(phydev, 0x88d9, 0xffff, 0x01a0); ++ rtl8125_phy_param(phydev, 0x8ffa, 0xffff, 0x002a); ++ ++ rtl8125_phy_param(phydev, 0x8fee, 0xffff, 0xffdf); ++ rtl8125_phy_param(phydev, 0x8ff0, 0xffff, 0xffff); ++ rtl8125_phy_param(phydev, 0x8ff2, 0xffff, 0x0a4a); ++ rtl8125_phy_param(phydev, 0x8ff4, 0xffff, 0xaa5a); ++ rtl8125_phy_param(phydev, 0x8ff6, 0xffff, 0x0a4a); ++ ++ rtl8125_phy_param(phydev, 0x8ff8, 0xffff, 0xaa5a); ++ rtl8125_phy_param(phydev, 0x88d5, 0xff00, 0x0200); ++ ++ r8168g_phy_param(phydev, 0x84bb, 0xff00, 0x0a00); ++ r8168g_phy_param(phydev, 0x84c0, 0xff00, 0x1600); ++ ++ phy_modify_paged(phydev, 0x0a43, 0x10, 0x0000, 0x0003); ++ ++ rtl8125_legacy_force_mode(phydev); ++ rtl8168g_disable_aldps(phydev); ++ rtl8125_common_config_eee_phy(phydev); ++} ++ + void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, + enum mac_version ver) + { +@@ -1181,6 +1346,7 @@ void r8169_hw_phy_config(struct rtl8169_ + [RTL_GIGA_MAC_VER_64] = rtl8125d_hw_phy_config, + [RTL_GIGA_MAC_VER_66] = rtl8125bp_hw_phy_config, + [RTL_GIGA_MAC_VER_70] = rtl8126a_hw_phy_config, ++ [RTL_GIGA_MAC_VER_80] = rtl8127a_1_hw_phy_config, + }; + + if (phy_configs[ver]) diff --git a/target/linux/generic/backport-6.12/781-01-v6.13-net-phy-realtek-read-duplex-and-gbit-master-from-PHY.patch b/target/linux/generic/backport-6.12/781-01-v6.13-net-phy-realtek-read-duplex-and-gbit-master-from-PHY.patch new file mode 100644 index 00000000000..31b8aabf7a7 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-01-v6.13-net-phy-realtek-read-duplex-and-gbit-master-from-PHY.patch @@ -0,0 +1,106 @@ +From 081c9c0265c91b8333165aa6230c20bcbc6f7cbf Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 10 Oct 2024 14:07:16 +0100 +Subject: [PATCH] net: phy: realtek: read duplex and gbit master from PHYSR + register + +The PHYSR MMD register is present and defined equally for all RTL82xx +Ethernet PHYs. +Read duplex and Gbit master bits from rtlgen_decode_speed() and rename +it to rtlgen_decode_physr(). + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/b9a76341da851a18c985bc4774fa295babec79bb.1728565530.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek.c | 41 +++++++++++++++++++++++++++++++-------- + 1 file changed, 33 insertions(+), 8 deletions(-) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -80,15 +80,18 @@ + + #define RTL822X_VND2_GANLPAR 0xa414 + +-#define RTL822X_VND2_PHYSR 0xa434 +- + #define RTL8366RB_POWER_SAVE 0x15 + #define RTL8366RB_POWER_SAVE_ON BIT(12) + + #define RTL9000A_GINMR 0x14 + #define RTL9000A_GINMR_LINK_STATUS BIT(4) + +-#define RTLGEN_SPEED_MASK 0x0630 ++#define RTL_VND2_PHYSR 0xa434 ++#define RTL_VND2_PHYSR_DUPLEX BIT(3) ++#define RTL_VND2_PHYSR_SPEEDL GENMASK(5, 4) ++#define RTL_VND2_PHYSR_SPEEDH GENMASK(10, 9) ++#define RTL_VND2_PHYSR_MASTER BIT(11) ++#define RTL_VND2_PHYSR_SPEED_MASK (RTL_VND2_PHYSR_SPEEDL | RTL_VND2_PHYSR_SPEEDH) + + #define RTL_GENERIC_PHYID 0x001cc800 + #define RTL_8211FVD_PHYID 0x001cc878 +@@ -661,9 +664,18 @@ static int rtl8366rb_config_init(struct + } + + /* get actual speed to cover the downshift case */ +-static void rtlgen_decode_speed(struct phy_device *phydev, int val) ++static void rtlgen_decode_physr(struct phy_device *phydev, int val) + { +- switch (val & RTLGEN_SPEED_MASK) { ++ /* bit 3 ++ * 0: Half Duplex ++ * 1: Full Duplex ++ */ ++ if (val & RTL_VND2_PHYSR_DUPLEX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ ++ switch (val & RTL_VND2_PHYSR_SPEED_MASK) { + case 0x0000: + phydev->speed = SPEED_10; + break; +@@ -685,6 +697,19 @@ static void rtlgen_decode_speed(struct p + default: + break; + } ++ ++ /* bit 11 ++ * 0: Slave Mode ++ * 1: Master Mode ++ */ ++ if (phydev->speed >= 1000) { ++ if (val & RTL_VND2_PHYSR_MASTER) ++ phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER; ++ else ++ phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE; ++ } else { ++ phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; ++ } + } + + static int rtlgen_read_status(struct phy_device *phydev) +@@ -702,7 +727,7 @@ static int rtlgen_read_status(struct phy + if (val < 0) + return val; + +- rtlgen_decode_speed(phydev, val); ++ rtlgen_decode_physr(phydev, val); + + return 0; + } +@@ -1008,11 +1033,11 @@ static int rtl822x_c45_read_status(struc + return 0; + + /* Read actual speed from vendor register. */ +- val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_PHYSR); ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_VND2_PHYSR); + if (val < 0) + return val; + +- rtlgen_decode_speed(phydev, val); ++ rtlgen_decode_physr(phydev, val); + + return 0; + } diff --git a/target/linux/generic/backport-6.12/781-02-v6.13-net-phy-realtek-change-order-of-calls-in-C22-read_st.patch b/target/linux/generic/backport-6.12/781-02-v6.13-net-phy-realtek-change-order-of-calls-in-C22-read_st.patch new file mode 100644 index 00000000000..2e5d7bb3b42 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-02-v6.13-net-phy-realtek-change-order-of-calls-in-C22-read_st.patch @@ -0,0 +1,53 @@ +From 68d5cd09e8919679ce13b85950debea4b2e98e04 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 10 Oct 2024 14:07:26 +0100 +Subject: [PATCH] net: phy: realtek: change order of calls in C22 read_status() + +Always call rtlgen_read_status() first, so genphy_read_status() which +is called by it clears bits in case auto-negotiation has not completed. +Also clear 10GBT link-partner advertisement bits in case auto-negotiation +is disabled or has not completed. + +Suggested-by: Russell King (Oracle) +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/b15929a41621d215c6b2b57393368086589569ec.1728565530.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek.c | 22 +++++++++++++++------- + 1 file changed, 15 insertions(+), 7 deletions(-) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -950,17 +950,25 @@ static void rtl822xb_update_interface(st + + static int rtl822x_read_status(struct phy_device *phydev) + { +- if (phydev->autoneg == AUTONEG_ENABLE) { +- int lpadv = phy_read_paged(phydev, 0xa5d, 0x13); ++ int lpadv, ret; + +- if (lpadv < 0) +- return lpadv; ++ ret = rtlgen_read_status(phydev); ++ if (ret < 0) ++ return ret; + +- mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, +- lpadv); ++ if (phydev->autoneg == AUTONEG_DISABLE || ++ !phydev->autoneg_complete) { ++ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0); ++ return 0; + } + +- return rtlgen_read_status(phydev); ++ lpadv = phy_read_paged(phydev, 0xa5d, 0x13); ++ if (lpadv < 0) ++ return lpadv; ++ ++ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv); ++ ++ return 0; + } + + static int rtl822xb_read_status(struct phy_device *phydev) diff --git a/target/linux/generic/backport-6.12/781-03-v6.13-net-phy-realtek-clear-1000Base-T-link-partner-advert.patch b/target/linux/generic/backport-6.12/781-03-v6.13-net-phy-realtek-clear-1000Base-T-link-partner-advert.patch new file mode 100644 index 00000000000..f7d8a5aa163 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-03-v6.13-net-phy-realtek-clear-1000Base-T-link-partner-advert.patch @@ -0,0 +1,30 @@ +From 5cb409b3960e75467cbb0a8e1e5596b4490570e3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 10 Oct 2024 14:07:39 +0100 +Subject: [PATCH] net: phy: realtek: clear 1000Base-T link partner + advertisement + +Clear 1000Base-T link partner advertisement bits in Clause-45 +read_status() function in case auto-negotiation is disabled or has not +been completed. + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/9dc9b47b2d675708afef3ad366bfd78eb584d958.1728565530.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -1027,6 +1027,10 @@ static int rtl822x_c45_read_status(struc + if (ret < 0) + return ret; + ++ if (phydev->autoneg == AUTONEG_DISABLE || ++ !genphy_c45_aneg_done(phydev)) ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, 0); ++ + /* Vendor register as C45 has no standardized support for 1000BaseT */ + if (phydev->autoneg == AUTONEG_ENABLE) { + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, diff --git a/target/linux/generic/backport-6.12/781-06-v6.14-net-phy-realtek-add-support-for-reading-MDIO_MMD_VEN.patch b/target/linux/generic/backport-6.12/781-06-v6.14-net-phy-realtek-add-support-for-reading-MDIO_MMD_VEN.patch new file mode 100644 index 00000000000..2add672f441 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-06-v6.14-net-phy-realtek-add-support-for-reading-MDIO_MMD_VEN.patch @@ -0,0 +1,47 @@ +From 3d483a10327f38595f714f9f9e9dde43a622cb0f Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Sat, 11 Jan 2025 21:49:31 +0100 +Subject: [PATCH] net: phy: realtek: add support for reading MDIO_MMD_VEND2 + regs on RTL8125/RTL8126 + +RTL8125/RTL8126 don't support MMD access to the internal PHY, but +provide a mechanism to access at least all MDIO_MMD_VEND2 registers. +By exposing this mechanism standard MMD access functions can be used +to access the MDIO_MMD_VEND2 registers. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/e821b302-5fe6-49ab-aabd-05da500581c0@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -736,7 +736,11 @@ static int rtlgen_read_mmd(struct phy_de + { + int ret; + +- if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE) { ++ if (devnum == MDIO_MMD_VEND2) { ++ rtl821x_write_page(phydev, regnum >> 4); ++ ret = __phy_read(phydev, 0x10 + ((regnum & 0xf) >> 1)); ++ rtl821x_write_page(phydev, 0); ++ } else if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE) { + rtl821x_write_page(phydev, 0xa5c); + ret = __phy_read(phydev, 0x12); + rtl821x_write_page(phydev, 0); +@@ -760,7 +764,11 @@ static int rtlgen_write_mmd(struct phy_d + { + int ret; + +- if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) { ++ if (devnum == MDIO_MMD_VEND2) { ++ rtl821x_write_page(phydev, regnum >> 4); ++ ret = __phy_write(phydev, 0x10 + ((regnum & 0xf) >> 1), val); ++ rtl821x_write_page(phydev, 0); ++ } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) { + rtl821x_write_page(phydev, 0xa5d); + ret = __phy_write(phydev, 0x10, val); + rtl821x_write_page(phydev, 0); diff --git a/target/linux/generic/backport-6.12/781-07-v6.14-net-phy-realtek-clear-1000Base-T-lpa-if-link-is-down.patch b/target/linux/generic/backport-6.12/781-07-v6.14-net-phy-realtek-clear-1000Base-T-lpa-if-link-is-down.patch new file mode 100644 index 00000000000..002b99e4d5c --- /dev/null +++ b/target/linux/generic/backport-6.12/781-07-v6.14-net-phy-realtek-clear-1000Base-T-lpa-if-link-is-down.patch @@ -0,0 +1,52 @@ +From 34d5a86ff7bbe225fba3ad91f9b4dc85fb408e18 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 15 Jan 2025 14:43:35 +0000 +Subject: [PATCH] net: phy: realtek: clear 1000Base-T lpa if link is down + +Only read 1000Base-T link partner advertisement if autonegotiation has +completed and otherwise 1000Base-T link partner advertisement bits. + +This fixes bogus 1000Base-T link partner advertisement after link goes +down (eg. by disconnecting the wire). +Fixes: 5cb409b3960e ("net: phy: realtek: clear 1000Base-T link partner advertisement") +Signed-off-by: Daniel Golle +Reviewed-by: Michal Swiatkowski +Signed-off-by: David S. Miller +--- + drivers/net/phy/realtek.c | 19 ++++++++----------- + 1 file changed, 8 insertions(+), 11 deletions(-) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -1031,23 +1031,20 @@ static int rtl822x_c45_read_status(struc + { + int ret, val; + +- ret = genphy_c45_read_status(phydev); +- if (ret < 0) +- return ret; +- +- if (phydev->autoneg == AUTONEG_DISABLE || +- !genphy_c45_aneg_done(phydev)) +- mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, 0); +- + /* Vendor register as C45 has no standardized support for 1000BaseT */ +- if (phydev->autoneg == AUTONEG_ENABLE) { ++ if (phydev->autoneg == AUTONEG_ENABLE && genphy_c45_aneg_done(phydev)) { + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, + RTL822X_VND2_GANLPAR); + if (val < 0) + return val; +- +- mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); ++ } else { ++ val = 0; + } ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); ++ ++ ret = genphy_c45_read_status(phydev); ++ if (ret < 0) ++ return ret; + + if (!phydev->link) + return 0; diff --git a/target/linux/generic/backport-6.12/781-08-v6.14-net-phy-realtek-clear-master_slave_state-if-link-is-.patch b/target/linux/generic/backport-6.12/781-08-v6.14-net-phy-realtek-clear-master_slave_state-if-link-is-.patch new file mode 100644 index 00000000000..f8cfc4d1316 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-08-v6.14-net-phy-realtek-clear-master_slave_state-if-link-is-.patch @@ -0,0 +1,35 @@ +From ea8318cb33e593bbfc59d637eae45a69732c5387 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 15 Jan 2025 14:43:43 +0000 +Subject: [PATCH] net: phy: realtek: clear master_slave_state if link is down + +rtlgen_decode_physr() which sets master_slave_state isn't called in case +the link is down and other than rtlgen_read_status(), +rtl822x_c45_read_status() doesn't implicitely clear master_slave_state. + +Avoid stale master_slave_state by always setting it to +MASTER_SLAVE_STATE_UNKNOWN in rtl822x_c45_read_status() in case the link +is down. + +Fixes: 081c9c0265c9 ("net: phy: realtek: read duplex and gbit master from PHYSR register") +Signed-off-by: Daniel Golle +Reviewed-by: Michal Swiatkowski +Signed-off-by: David S. Miller +--- + drivers/net/phy/realtek.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -1046,8 +1046,10 @@ static int rtl822x_c45_read_status(struc + if (ret < 0) + return ret; + +- if (!phydev->link) ++ if (!phydev->link) { ++ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; + return 0; ++ } + + /* Read actual speed from vendor register. */ + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_VND2_PHYSR); diff --git a/target/linux/generic/backport-6.12/781-09-v6.14-net-phy-realtek-always-clear-NBase-T-lpa.patch b/target/linux/generic/backport-6.12/781-09-v6.14-net-phy-realtek-always-clear-NBase-T-lpa.patch new file mode 100644 index 00000000000..e628bed6b4c --- /dev/null +++ b/target/linux/generic/backport-6.12/781-09-v6.14-net-phy-realtek-always-clear-NBase-T-lpa.patch @@ -0,0 +1,42 @@ +From d3eb58549842c60ed46f37da7f4da969e3d6ecd3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 15 Jan 2025 14:45:00 +0000 +Subject: [PATCH] net: phy: realtek: always clear NBase-T lpa + +Clear NBase-T link partner advertisement before calling +rtlgen_read_status() to avoid phy_resolve_aneg_linkmode() wrongly +setting speed and duplex. + +This fixes bogus 2.5G/5G/10G link partner advertisement and thus +speed and duplex being set by phy_resolve_aneg_linkmode() due to stale +NBase-T lpa. + +Fixes: 68d5cd09e891 ("net: phy: realtek: change order of calls in C22 read_status()") +Signed-off-by: Daniel Golle +Reviewed-by: Michal Swiatkowski +Signed-off-by: David S. Miller +--- + drivers/net/phy/realtek.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -960,15 +960,15 @@ static int rtl822x_read_status(struct ph + { + int lpadv, ret; + ++ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0); ++ + ret = rtlgen_read_status(phydev); + if (ret < 0) + return ret; + + if (phydev->autoneg == AUTONEG_DISABLE || +- !phydev->autoneg_complete) { +- mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0); ++ !phydev->autoneg_complete) + return 0; +- } + + lpadv = phy_read_paged(phydev, 0xa5d, 0x13); + if (lpadv < 0) diff --git a/target/linux/generic/backport-6.12/781-10-v6.14-net-phy-move-realtek-PHY-driver-to-its-own-subdirect.patch b/target/linux/generic/backport-6.12/781-10-v6.14-net-phy-move-realtek-PHY-driver-to-its-own-subdirect.patch new file mode 100644 index 00000000000..633468f93a3 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-10-v6.14-net-phy-move-realtek-PHY-driver-to-its-own-subdirect.patch @@ -0,0 +1,3247 @@ +From 1416a9b2ba710d31954131c06d46f298e340aa2c Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Sat, 11 Jan 2025 21:50:19 +0100 +Subject: [PATCH] net: phy: move realtek PHY driver to its own subdirectory + +In preparation of adding a source file with hwmon support, move the +Realtek PHY driver to its own subdirectory and rename realtek.c to +realtek_main.c. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/c566551b-c915-4e34-9b33-129a6ddd6e4c@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/Kconfig | 5 +---- + drivers/net/phy/Makefile | 2 +- + drivers/net/phy/realtek/Kconfig | 5 +++++ + drivers/net/phy/realtek/Makefile | 3 +++ + drivers/net/phy/{realtek.c => realtek/realtek_main.c} | 0 + 5 files changed, 10 insertions(+), 5 deletions(-) + create mode 100644 drivers/net/phy/realtek/Kconfig + create mode 100644 drivers/net/phy/realtek/Makefile + rename drivers/net/phy/{realtek.c => realtek/realtek_main.c} (100%) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -343,10 +343,7 @@ config QSEMI_PHY + help + Currently supports the qs6612 + +-config REALTEK_PHY +- tristate "Realtek PHYs" +- help +- Supports the Realtek 821x PHY. ++source "drivers/net/phy/realtek/Kconfig" + + config RENESAS_PHY + tristate "Renesas PHYs" +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -94,7 +94,7 @@ obj-$(CONFIG_NXP_CBTX_PHY) += nxp-cbtx.o + obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o + obj-y += qcom/ + obj-$(CONFIG_QSEMI_PHY) += qsemi.o +-obj-$(CONFIG_REALTEK_PHY) += realtek.o ++obj-$(CONFIG_REALTEK_PHY) += realtek/ + obj-$(CONFIG_RENESAS_PHY) += uPD60620.o + obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o + obj-$(CONFIG_SMSC_PHY) += smsc.o +--- /dev/null ++++ b/drivers/net/phy/realtek/Kconfig +@@ -0,0 +1,5 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++config REALTEK_PHY ++ tristate "Realtek PHYs" ++ help ++ Currently supports RTL821x/RTL822x and fast ethernet PHYs +--- /dev/null ++++ b/drivers/net/phy/realtek/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0 ++realtek-y += realtek_main.o ++obj-$(CONFIG_REALTEK_PHY) += realtek.o +--- a/drivers/net/phy/realtek.c ++++ /dev/null +@@ -1,1589 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-/* drivers/net/phy/realtek.c +- * +- * Driver for Realtek PHYs +- * +- * Author: Johnson Leung +- * +- * Copyright (c) 2004 Freescale Semiconductor, Inc. +- */ +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define RTL821x_PHYSR 0x11 +-#define RTL821x_PHYSR_DUPLEX BIT(13) +-#define RTL821x_PHYSR_SPEED GENMASK(15, 14) +- +-#define RTL821x_INER 0x12 +-#define RTL8211B_INER_INIT 0x6400 +-#define RTL8211E_INER_LINK_STATUS BIT(10) +-#define RTL8211F_INER_LINK_STATUS BIT(4) +- +-#define RTL821x_INSR 0x13 +- +-#define RTL821x_EXT_PAGE_SELECT 0x1e +-#define RTL821x_PAGE_SELECT 0x1f +- +-#define RTL8211F_PHYCR1 0x18 +-#define RTL8211F_PHYCR2 0x19 +-#define RTL8211F_INSR 0x1d +- +-#define RTL8211F_LEDCR 0x10 +-#define RTL8211F_LEDCR_MODE BIT(15) +-#define RTL8211F_LEDCR_ACT_TXRX BIT(4) +-#define RTL8211F_LEDCR_LINK_1000 BIT(3) +-#define RTL8211F_LEDCR_LINK_100 BIT(1) +-#define RTL8211F_LEDCR_LINK_10 BIT(0) +-#define RTL8211F_LEDCR_MASK GENMASK(4, 0) +-#define RTL8211F_LEDCR_SHIFT 5 +- +-#define RTL8211F_TX_DELAY BIT(8) +-#define RTL8211F_RX_DELAY BIT(3) +- +-#define RTL8211F_ALDPS_PLL_OFF BIT(1) +-#define RTL8211F_ALDPS_ENABLE BIT(2) +-#define RTL8211F_ALDPS_XTAL_OFF BIT(12) +- +-#define RTL8211E_CTRL_DELAY BIT(13) +-#define RTL8211E_TX_DELAY BIT(12) +-#define RTL8211E_RX_DELAY BIT(11) +- +-#define RTL8211F_CLKOUT_EN BIT(0) +- +-#define RTL8201F_ISR 0x1e +-#define RTL8201F_ISR_ANERR BIT(15) +-#define RTL8201F_ISR_DUPLEX BIT(13) +-#define RTL8201F_ISR_LINK BIT(11) +-#define RTL8201F_ISR_MASK (RTL8201F_ISR_ANERR | \ +- RTL8201F_ISR_DUPLEX | \ +- RTL8201F_ISR_LINK) +-#define RTL8201F_IER 0x13 +- +-#define RTL822X_VND1_SERDES_OPTION 0x697a +-#define RTL822X_VND1_SERDES_OPTION_MODE_MASK GENMASK(5, 0) +-#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII 0 +-#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX 2 +- +-#define RTL822X_VND1_SERDES_CTRL3 0x7580 +-#define RTL822X_VND1_SERDES_CTRL3_MODE_MASK GENMASK(5, 0) +-#define RTL822X_VND1_SERDES_CTRL3_MODE_SGMII 0x02 +-#define RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX 0x16 +- +-/* RTL822X_VND2_XXXXX registers are only accessible when phydev->is_c45 +- * is set, they cannot be accessed by C45-over-C22. +- */ +-#define RTL822X_VND2_GBCR 0xa412 +- +-#define RTL822X_VND2_GANLPAR 0xa414 +- +-#define RTL8366RB_POWER_SAVE 0x15 +-#define RTL8366RB_POWER_SAVE_ON BIT(12) +- +-#define RTL9000A_GINMR 0x14 +-#define RTL9000A_GINMR_LINK_STATUS BIT(4) +- +-#define RTL_VND2_PHYSR 0xa434 +-#define RTL_VND2_PHYSR_DUPLEX BIT(3) +-#define RTL_VND2_PHYSR_SPEEDL GENMASK(5, 4) +-#define RTL_VND2_PHYSR_SPEEDH GENMASK(10, 9) +-#define RTL_VND2_PHYSR_MASTER BIT(11) +-#define RTL_VND2_PHYSR_SPEED_MASK (RTL_VND2_PHYSR_SPEEDL | RTL_VND2_PHYSR_SPEEDH) +- +-#define RTL_GENERIC_PHYID 0x001cc800 +-#define RTL_8211FVD_PHYID 0x001cc878 +-#define RTL_8221B 0x001cc840 +-#define RTL_8221B_VB_CG 0x001cc849 +-#define RTL_8221B_VN_CG 0x001cc84a +-#define RTL_8251B 0x001cc862 +- +-#define RTL8211F_LED_COUNT 3 +- +-MODULE_DESCRIPTION("Realtek PHY driver"); +-MODULE_AUTHOR("Johnson Leung"); +-MODULE_LICENSE("GPL"); +- +-struct rtl821x_priv { +- u16 phycr1; +- u16 phycr2; +- bool has_phycr2; +- struct clk *clk; +-}; +- +-static int rtl821x_read_page(struct phy_device *phydev) +-{ +- return __phy_read(phydev, RTL821x_PAGE_SELECT); +-} +- +-static int rtl821x_write_page(struct phy_device *phydev, int page) +-{ +- return __phy_write(phydev, RTL821x_PAGE_SELECT, page); +-} +- +-static int rtl821x_probe(struct phy_device *phydev) +-{ +- struct device *dev = &phydev->mdio.dev; +- struct rtl821x_priv *priv; +- u32 phy_id = phydev->drv->phy_id; +- int ret; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->clk = devm_clk_get_optional_enabled(dev, NULL); +- if (IS_ERR(priv->clk)) +- return dev_err_probe(dev, PTR_ERR(priv->clk), +- "failed to get phy clock\n"); +- +- ret = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR1); +- if (ret < 0) +- return ret; +- +- priv->phycr1 = ret & (RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF); +- if (of_property_read_bool(dev->of_node, "realtek,aldps-enable")) +- priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF; +- +- priv->has_phycr2 = !(phy_id == RTL_8211FVD_PHYID); +- if (priv->has_phycr2) { +- ret = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR2); +- if (ret < 0) +- return ret; +- +- priv->phycr2 = ret & RTL8211F_CLKOUT_EN; +- if (of_property_read_bool(dev->of_node, "realtek,clkout-disable")) +- priv->phycr2 &= ~RTL8211F_CLKOUT_EN; +- } +- +- phydev->priv = priv; +- +- return 0; +-} +- +-static int rtl8201_ack_interrupt(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_read(phydev, RTL8201F_ISR); +- +- return (err < 0) ? err : 0; +-} +- +-static int rtl821x_ack_interrupt(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_read(phydev, RTL821x_INSR); +- +- return (err < 0) ? err : 0; +-} +- +-static int rtl8211f_ack_interrupt(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_read_paged(phydev, 0xa43, RTL8211F_INSR); +- +- return (err < 0) ? err : 0; +-} +- +-static int rtl8201_config_intr(struct phy_device *phydev) +-{ +- u16 val; +- int err; +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- err = rtl8201_ack_interrupt(phydev); +- if (err) +- return err; +- +- val = BIT(13) | BIT(12) | BIT(11); +- err = phy_write_paged(phydev, 0x7, RTL8201F_IER, val); +- } else { +- val = 0; +- err = phy_write_paged(phydev, 0x7, RTL8201F_IER, val); +- if (err) +- return err; +- +- err = rtl8201_ack_interrupt(phydev); +- } +- +- return err; +-} +- +-static int rtl8211b_config_intr(struct phy_device *phydev) +-{ +- int err; +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- err = rtl821x_ack_interrupt(phydev); +- if (err) +- return err; +- +- err = phy_write(phydev, RTL821x_INER, +- RTL8211B_INER_INIT); +- } else { +- err = phy_write(phydev, RTL821x_INER, 0); +- if (err) +- return err; +- +- err = rtl821x_ack_interrupt(phydev); +- } +- +- return err; +-} +- +-static int rtl8211e_config_intr(struct phy_device *phydev) +-{ +- int err; +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- err = rtl821x_ack_interrupt(phydev); +- if (err) +- return err; +- +- err = phy_write(phydev, RTL821x_INER, +- RTL8211E_INER_LINK_STATUS); +- } else { +- err = phy_write(phydev, RTL821x_INER, 0); +- if (err) +- return err; +- +- err = rtl821x_ack_interrupt(phydev); +- } +- +- return err; +-} +- +-static int rtl8211f_config_intr(struct phy_device *phydev) +-{ +- u16 val; +- int err; +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- err = rtl8211f_ack_interrupt(phydev); +- if (err) +- return err; +- +- val = RTL8211F_INER_LINK_STATUS; +- err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val); +- } else { +- val = 0; +- err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val); +- if (err) +- return err; +- +- err = rtl8211f_ack_interrupt(phydev); +- } +- +- return err; +-} +- +-static irqreturn_t rtl8201_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status; +- +- irq_status = phy_read(phydev, RTL8201F_ISR); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- if (!(irq_status & RTL8201F_ISR_MASK)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- +-static irqreturn_t rtl821x_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status, irq_enabled; +- +- irq_status = phy_read(phydev, RTL821x_INSR); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- irq_enabled = phy_read(phydev, RTL821x_INER); +- if (irq_enabled < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- if (!(irq_status & irq_enabled)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- +-static irqreturn_t rtl8211f_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status; +- +- irq_status = phy_read_paged(phydev, 0xa43, RTL8211F_INSR); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- if (!(irq_status & RTL8211F_INER_LINK_STATUS)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- +-static int rtl8211_config_aneg(struct phy_device *phydev) +-{ +- int ret; +- +- ret = genphy_config_aneg(phydev); +- if (ret < 0) +- return ret; +- +- /* Quirk was copied from vendor driver. Unfortunately it includes no +- * description of the magic numbers. +- */ +- if (phydev->speed == SPEED_100 && phydev->autoneg == AUTONEG_DISABLE) { +- phy_write(phydev, 0x17, 0x2138); +- phy_write(phydev, 0x0e, 0x0260); +- } else { +- phy_write(phydev, 0x17, 0x2108); +- phy_write(phydev, 0x0e, 0x0000); +- } +- +- return 0; +-} +- +-static int rtl8211c_config_init(struct phy_device *phydev) +-{ +- /* RTL8211C has an issue when operating in Gigabit slave mode */ +- return phy_set_bits(phydev, MII_CTRL1000, +- CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER); +-} +- +-static int rtl8211f_config_init(struct phy_device *phydev) +-{ +- struct rtl821x_priv *priv = phydev->priv; +- struct device *dev = &phydev->mdio.dev; +- u16 val_txdly, val_rxdly; +- int ret; +- +- ret = phy_modify_paged_changed(phydev, 0xa43, RTL8211F_PHYCR1, +- RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF, +- priv->phycr1); +- if (ret < 0) { +- dev_err(dev, "aldps mode configuration failed: %pe\n", +- ERR_PTR(ret)); +- return ret; +- } +- +- switch (phydev->interface) { +- case PHY_INTERFACE_MODE_RGMII: +- val_txdly = 0; +- val_rxdly = 0; +- break; +- +- case PHY_INTERFACE_MODE_RGMII_RXID: +- val_txdly = 0; +- val_rxdly = RTL8211F_RX_DELAY; +- break; +- +- case PHY_INTERFACE_MODE_RGMII_TXID: +- val_txdly = RTL8211F_TX_DELAY; +- val_rxdly = 0; +- break; +- +- case PHY_INTERFACE_MODE_RGMII_ID: +- val_txdly = RTL8211F_TX_DELAY; +- val_rxdly = RTL8211F_RX_DELAY; +- break; +- +- default: /* the rest of the modes imply leaving delay as is. */ +- return 0; +- } +- +- ret = phy_modify_paged_changed(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, +- val_txdly); +- if (ret < 0) { +- dev_err(dev, "Failed to update the TX delay register\n"); +- return ret; +- } else if (ret) { +- dev_dbg(dev, +- "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n", +- val_txdly ? "Enabling" : "Disabling"); +- } else { +- dev_dbg(dev, +- "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n", +- val_txdly ? "enabled" : "disabled"); +- } +- +- ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY, +- val_rxdly); +- if (ret < 0) { +- dev_err(dev, "Failed to update the RX delay register\n"); +- return ret; +- } else if (ret) { +- dev_dbg(dev, +- "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n", +- val_rxdly ? "Enabling" : "Disabling"); +- } else { +- dev_dbg(dev, +- "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n", +- val_rxdly ? "enabled" : "disabled"); +- } +- +- if (priv->has_phycr2) { +- ret = phy_modify_paged(phydev, 0xa43, RTL8211F_PHYCR2, +- RTL8211F_CLKOUT_EN, priv->phycr2); +- if (ret < 0) { +- dev_err(dev, "clkout configuration failed: %pe\n", +- ERR_PTR(ret)); +- return ret; +- } +- +- return genphy_soft_reset(phydev); +- } +- +- return 0; +-} +- +-static int rtl821x_suspend(struct phy_device *phydev) +-{ +- struct rtl821x_priv *priv = phydev->priv; +- int ret = 0; +- +- if (!phydev->wol_enabled) { +- ret = genphy_suspend(phydev); +- +- if (ret) +- return ret; +- +- clk_disable_unprepare(priv->clk); +- } +- +- return ret; +-} +- +-static int rtl821x_resume(struct phy_device *phydev) +-{ +- struct rtl821x_priv *priv = phydev->priv; +- int ret; +- +- if (!phydev->wol_enabled) +- clk_prepare_enable(priv->clk); +- +- ret = genphy_resume(phydev); +- if (ret < 0) +- return ret; +- +- msleep(20); +- +- return 0; +-} +- +-static int rtl8211f_led_hw_is_supported(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- const unsigned long mask = BIT(TRIGGER_NETDEV_LINK_10) | +- BIT(TRIGGER_NETDEV_LINK_100) | +- BIT(TRIGGER_NETDEV_LINK_1000) | +- BIT(TRIGGER_NETDEV_RX) | +- BIT(TRIGGER_NETDEV_TX); +- +- /* The RTL8211F PHY supports these LED settings on up to three LEDs: +- * - Link: Configurable subset of 10/100/1000 link rates +- * - Active: Blink on activity, RX or TX is not differentiated +- * The Active option has two modes, A and B: +- * - A: Link and Active indication at configurable, but matching, +- * subset of 10/100/1000 link rates +- * - B: Link indication at configurable subset of 10/100/1000 link +- * rates and Active indication always at all three 10+100+1000 +- * link rates. +- * This code currently uses mode B only. +- */ +- +- if (index >= RTL8211F_LED_COUNT) +- return -EINVAL; +- +- /* Filter out any other unsupported triggers. */ +- if (rules & ~mask) +- return -EOPNOTSUPP; +- +- /* RX and TX are not differentiated, either both are set or not set. */ +- if (!(rules & BIT(TRIGGER_NETDEV_RX)) ^ !(rules & BIT(TRIGGER_NETDEV_TX))) +- return -EOPNOTSUPP; +- +- return 0; +-} +- +-static int rtl8211f_led_hw_control_get(struct phy_device *phydev, u8 index, +- unsigned long *rules) +-{ +- int val; +- +- if (index >= RTL8211F_LED_COUNT) +- return -EINVAL; +- +- val = phy_read_paged(phydev, 0xd04, RTL8211F_LEDCR); +- if (val < 0) +- return val; +- +- val >>= RTL8211F_LEDCR_SHIFT * index; +- val &= RTL8211F_LEDCR_MASK; +- +- if (val & RTL8211F_LEDCR_LINK_10) +- set_bit(TRIGGER_NETDEV_LINK_10, rules); +- +- if (val & RTL8211F_LEDCR_LINK_100) +- set_bit(TRIGGER_NETDEV_LINK_100, rules); +- +- if (val & RTL8211F_LEDCR_LINK_1000) +- set_bit(TRIGGER_NETDEV_LINK_1000, rules); +- +- if (val & RTL8211F_LEDCR_ACT_TXRX) { +- set_bit(TRIGGER_NETDEV_RX, rules); +- set_bit(TRIGGER_NETDEV_TX, rules); +- } +- +- return 0; +-} +- +-static int rtl8211f_led_hw_control_set(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- const u16 mask = RTL8211F_LEDCR_MASK << (RTL8211F_LEDCR_SHIFT * index); +- u16 reg = 0; +- +- if (index >= RTL8211F_LED_COUNT) +- return -EINVAL; +- +- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) +- reg |= RTL8211F_LEDCR_LINK_10; +- +- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) +- reg |= RTL8211F_LEDCR_LINK_100; +- +- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) +- reg |= RTL8211F_LEDCR_LINK_1000; +- +- if (test_bit(TRIGGER_NETDEV_RX, &rules) || +- test_bit(TRIGGER_NETDEV_TX, &rules)) { +- reg |= RTL8211F_LEDCR_ACT_TXRX; +- } +- +- reg <<= RTL8211F_LEDCR_SHIFT * index; +- reg |= RTL8211F_LEDCR_MODE; /* Mode B */ +- +- return phy_modify_paged(phydev, 0xd04, RTL8211F_LEDCR, mask, reg); +-} +- +-static int rtl8211e_config_init(struct phy_device *phydev) +-{ +- int ret = 0, oldpage; +- u16 val; +- +- /* enable TX/RX delay for rgmii-* modes, and disable them for rgmii. */ +- switch (phydev->interface) { +- case PHY_INTERFACE_MODE_RGMII: +- val = RTL8211E_CTRL_DELAY | 0; +- break; +- case PHY_INTERFACE_MODE_RGMII_ID: +- val = RTL8211E_CTRL_DELAY | RTL8211E_TX_DELAY | RTL8211E_RX_DELAY; +- break; +- case PHY_INTERFACE_MODE_RGMII_RXID: +- val = RTL8211E_CTRL_DELAY | RTL8211E_RX_DELAY; +- break; +- case PHY_INTERFACE_MODE_RGMII_TXID: +- val = RTL8211E_CTRL_DELAY | RTL8211E_TX_DELAY; +- break; +- default: /* the rest of the modes imply leaving delays as is. */ +- return 0; +- } +- +- /* According to a sample driver there is a 0x1c config register on the +- * 0xa4 extension page (0x7) layout. It can be used to disable/enable +- * the RX/TX delays otherwise controlled by RXDLY/TXDLY pins. +- * The configuration register definition: +- * 14 = reserved +- * 13 = Force Tx RX Delay controlled by bit12 bit11, +- * 12 = RX Delay, 11 = TX Delay +- * 10:0 = Test && debug settings reserved by realtek +- */ +- oldpage = phy_select_page(phydev, 0x7); +- if (oldpage < 0) +- goto err_restore_page; +- +- ret = __phy_write(phydev, RTL821x_EXT_PAGE_SELECT, 0xa4); +- if (ret) +- goto err_restore_page; +- +- ret = __phy_modify(phydev, 0x1c, RTL8211E_CTRL_DELAY +- | RTL8211E_TX_DELAY | RTL8211E_RX_DELAY, +- val); +- +-err_restore_page: +- return phy_restore_page(phydev, oldpage, ret); +-} +- +-static int rtl8211b_suspend(struct phy_device *phydev) +-{ +- phy_write(phydev, MII_MMD_DATA, BIT(9)); +- +- return genphy_suspend(phydev); +-} +- +-static int rtl8211b_resume(struct phy_device *phydev) +-{ +- phy_write(phydev, MII_MMD_DATA, 0); +- +- return genphy_resume(phydev); +-} +- +-static int rtl8366rb_config_init(struct phy_device *phydev) +-{ +- int ret; +- +- ret = phy_set_bits(phydev, RTL8366RB_POWER_SAVE, +- RTL8366RB_POWER_SAVE_ON); +- if (ret) { +- dev_err(&phydev->mdio.dev, +- "error enabling power management\n"); +- } +- +- return ret; +-} +- +-/* get actual speed to cover the downshift case */ +-static void rtlgen_decode_physr(struct phy_device *phydev, int val) +-{ +- /* bit 3 +- * 0: Half Duplex +- * 1: Full Duplex +- */ +- if (val & RTL_VND2_PHYSR_DUPLEX) +- phydev->duplex = DUPLEX_FULL; +- else +- phydev->duplex = DUPLEX_HALF; +- +- switch (val & RTL_VND2_PHYSR_SPEED_MASK) { +- case 0x0000: +- phydev->speed = SPEED_10; +- break; +- case 0x0010: +- phydev->speed = SPEED_100; +- break; +- case 0x0020: +- phydev->speed = SPEED_1000; +- break; +- case 0x0200: +- phydev->speed = SPEED_10000; +- break; +- case 0x0210: +- phydev->speed = SPEED_2500; +- break; +- case 0x0220: +- phydev->speed = SPEED_5000; +- break; +- default: +- break; +- } +- +- /* bit 11 +- * 0: Slave Mode +- * 1: Master Mode +- */ +- if (phydev->speed >= 1000) { +- if (val & RTL_VND2_PHYSR_MASTER) +- phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER; +- else +- phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE; +- } else { +- phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; +- } +-} +- +-static int rtlgen_read_status(struct phy_device *phydev) +-{ +- int ret, val; +- +- ret = genphy_read_status(phydev); +- if (ret < 0) +- return ret; +- +- if (!phydev->link) +- return 0; +- +- val = phy_read_paged(phydev, 0xa43, 0x12); +- if (val < 0) +- return val; +- +- rtlgen_decode_physr(phydev, val); +- +- return 0; +-} +- +-static int rtlgen_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) +-{ +- int ret; +- +- if (devnum == MDIO_MMD_VEND2) { +- rtl821x_write_page(phydev, regnum >> 4); +- ret = __phy_read(phydev, 0x10 + ((regnum & 0xf) >> 1)); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE) { +- rtl821x_write_page(phydev, 0xa5c); +- ret = __phy_read(phydev, 0x12); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) { +- rtl821x_write_page(phydev, 0xa5d); +- ret = __phy_read(phydev, 0x10); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE) { +- rtl821x_write_page(phydev, 0xa5d); +- ret = __phy_read(phydev, 0x11); +- rtl821x_write_page(phydev, 0); +- } else { +- ret = -EOPNOTSUPP; +- } +- +- return ret; +-} +- +-static int rtlgen_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, +- u16 val) +-{ +- int ret; +- +- if (devnum == MDIO_MMD_VEND2) { +- rtl821x_write_page(phydev, regnum >> 4); +- ret = __phy_write(phydev, 0x10 + ((regnum & 0xf) >> 1), val); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) { +- rtl821x_write_page(phydev, 0xa5d); +- ret = __phy_write(phydev, 0x10, val); +- rtl821x_write_page(phydev, 0); +- } else { +- ret = -EOPNOTSUPP; +- } +- +- return ret; +-} +- +-static int rtl822x_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) +-{ +- int ret = rtlgen_read_mmd(phydev, devnum, regnum); +- +- if (ret != -EOPNOTSUPP) +- return ret; +- +- if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE2) { +- rtl821x_write_page(phydev, 0xa6e); +- ret = __phy_read(phydev, 0x16); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV2) { +- rtl821x_write_page(phydev, 0xa6d); +- ret = __phy_read(phydev, 0x12); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE2) { +- rtl821x_write_page(phydev, 0xa6d); +- ret = __phy_read(phydev, 0x10); +- rtl821x_write_page(phydev, 0); +- } +- +- return ret; +-} +- +-static int rtl822x_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, +- u16 val) +-{ +- int ret = rtlgen_write_mmd(phydev, devnum, regnum, val); +- +- if (ret != -EOPNOTSUPP) +- return ret; +- +- if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV2) { +- rtl821x_write_page(phydev, 0xa6d); +- ret = __phy_write(phydev, 0x12, val); +- rtl821x_write_page(phydev, 0); +- } +- +- return ret; +-} +- +-static int rtl822xb_config_init(struct phy_device *phydev) +-{ +- bool has_2500, has_sgmii; +- u16 mode; +- int ret; +- +- has_2500 = test_bit(PHY_INTERFACE_MODE_2500BASEX, +- phydev->host_interfaces) || +- phydev->interface == PHY_INTERFACE_MODE_2500BASEX; +- +- has_sgmii = test_bit(PHY_INTERFACE_MODE_SGMII, +- phydev->host_interfaces) || +- phydev->interface == PHY_INTERFACE_MODE_SGMII; +- +- /* fill in possible interfaces */ +- __assign_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->possible_interfaces, +- has_2500); +- __assign_bit(PHY_INTERFACE_MODE_SGMII, phydev->possible_interfaces, +- has_sgmii); +- +- if (!has_2500 && !has_sgmii) +- return 0; +- +- /* determine SerDes option mode */ +- if (has_2500 && !has_sgmii) { +- mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX; +- phydev->rate_matching = RATE_MATCH_PAUSE; +- } else { +- mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII; +- phydev->rate_matching = RATE_MATCH_NONE; +- } +- +- /* the following sequence with magic numbers sets up the SerDes +- * option mode +- */ +- ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75f3, 0); +- if (ret < 0) +- return ret; +- +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1, +- RTL822X_VND1_SERDES_OPTION, +- RTL822X_VND1_SERDES_OPTION_MODE_MASK, +- mode); +- if (ret < 0) +- return ret; +- +- ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6a04, 0x0503); +- if (ret < 0) +- return ret; +- +- ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f10, 0xd455); +- if (ret < 0) +- return ret; +- +- return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020); +-} +- +-static int rtl822xb_get_rate_matching(struct phy_device *phydev, +- phy_interface_t iface) +-{ +- int val; +- +- /* Only rate matching at 2500base-x */ +- if (iface != PHY_INTERFACE_MODE_2500BASEX) +- return RATE_MATCH_NONE; +- +- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_OPTION); +- if (val < 0) +- return val; +- +- if ((val & RTL822X_VND1_SERDES_OPTION_MODE_MASK) == +- RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX) +- return RATE_MATCH_PAUSE; +- +- /* RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII */ +- return RATE_MATCH_NONE; +-} +- +-static int rtl822x_get_features(struct phy_device *phydev) +-{ +- int val; +- +- val = phy_read_paged(phydev, 0xa61, 0x13); +- if (val < 0) +- return val; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, +- phydev->supported, val & MDIO_PMA_SPEED_2_5G); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, +- phydev->supported, val & MDIO_PMA_SPEED_5G); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, +- phydev->supported, val & MDIO_SPEED_10G); +- +- return genphy_read_abilities(phydev); +-} +- +-static int rtl822x_config_aneg(struct phy_device *phydev) +-{ +- int ret = 0; +- +- if (phydev->autoneg == AUTONEG_ENABLE) { +- u16 adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); +- +- ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12, +- MDIO_AN_10GBT_CTRL_ADV2_5G | +- MDIO_AN_10GBT_CTRL_ADV5G, +- adv); +- if (ret < 0) +- return ret; +- } +- +- return __genphy_config_aneg(phydev, ret); +-} +- +-static void rtl822xb_update_interface(struct phy_device *phydev) +-{ +- int val; +- +- if (!phydev->link) +- return; +- +- /* Change interface according to serdes mode */ +- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_CTRL3); +- if (val < 0) +- return; +- +- switch (val & RTL822X_VND1_SERDES_CTRL3_MODE_MASK) { +- case RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX: +- phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +- break; +- case RTL822X_VND1_SERDES_CTRL3_MODE_SGMII: +- phydev->interface = PHY_INTERFACE_MODE_SGMII; +- break; +- } +-} +- +-static int rtl822x_read_status(struct phy_device *phydev) +-{ +- int lpadv, ret; +- +- mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0); +- +- ret = rtlgen_read_status(phydev); +- if (ret < 0) +- return ret; +- +- if (phydev->autoneg == AUTONEG_DISABLE || +- !phydev->autoneg_complete) +- return 0; +- +- lpadv = phy_read_paged(phydev, 0xa5d, 0x13); +- if (lpadv < 0) +- return lpadv; +- +- mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv); +- +- return 0; +-} +- +-static int rtl822xb_read_status(struct phy_device *phydev) +-{ +- int ret; +- +- ret = rtl822x_read_status(phydev); +- if (ret < 0) +- return ret; +- +- rtl822xb_update_interface(phydev); +- +- return 0; +-} +- +-static int rtl822x_c45_get_features(struct phy_device *phydev) +-{ +- linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, +- phydev->supported); +- +- return genphy_c45_pma_read_abilities(phydev); +-} +- +-static int rtl822x_c45_config_aneg(struct phy_device *phydev) +-{ +- bool changed = false; +- int ret, val; +- +- if (phydev->autoneg == AUTONEG_DISABLE) +- return genphy_c45_pma_setup_forced(phydev); +- +- ret = genphy_c45_an_config_aneg(phydev); +- if (ret < 0) +- return ret; +- if (ret > 0) +- changed = true; +- +- val = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); +- +- /* Vendor register as C45 has no standardized support for 1000BaseT */ +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL822X_VND2_GBCR, +- ADVERTISE_1000FULL, val); +- if (ret < 0) +- return ret; +- if (ret > 0) +- changed = true; +- +- return genphy_c45_check_and_restart_aneg(phydev, changed); +-} +- +-static int rtl822x_c45_read_status(struct phy_device *phydev) +-{ +- int ret, val; +- +- /* Vendor register as C45 has no standardized support for 1000BaseT */ +- if (phydev->autoneg == AUTONEG_ENABLE && genphy_c45_aneg_done(phydev)) { +- val = phy_read_mmd(phydev, MDIO_MMD_VEND2, +- RTL822X_VND2_GANLPAR); +- if (val < 0) +- return val; +- } else { +- val = 0; +- } +- mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); +- +- ret = genphy_c45_read_status(phydev); +- if (ret < 0) +- return ret; +- +- if (!phydev->link) { +- phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; +- return 0; +- } +- +- /* Read actual speed from vendor register. */ +- val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_VND2_PHYSR); +- if (val < 0) +- return val; +- +- rtlgen_decode_physr(phydev, val); +- +- return 0; +-} +- +-static int rtl822xb_c45_read_status(struct phy_device *phydev) +-{ +- int ret; +- +- ret = rtl822x_c45_read_status(phydev); +- if (ret < 0) +- return ret; +- +- rtl822xb_update_interface(phydev); +- +- return 0; +-} +- +-static bool rtlgen_supports_2_5gbps(struct phy_device *phydev) +-{ +- int val; +- +- phy_write(phydev, RTL821x_PAGE_SELECT, 0xa61); +- val = phy_read(phydev, 0x13); +- phy_write(phydev, RTL821x_PAGE_SELECT, 0); +- +- return val >= 0 && val & MDIO_PMA_SPEED_2_5G; +-} +- +-/* On internal PHY's MMD reads over C22 always return 0. +- * Check a MMD register which is known to be non-zero. +- */ +-static bool rtlgen_supports_mmd(struct phy_device *phydev) +-{ +- int val; +- +- phy_lock_mdio_bus(phydev); +- __phy_write(phydev, MII_MMD_CTRL, MDIO_MMD_PCS); +- __phy_write(phydev, MII_MMD_DATA, MDIO_PCS_EEE_ABLE); +- __phy_write(phydev, MII_MMD_CTRL, MDIO_MMD_PCS | MII_MMD_CTRL_NOINCR); +- val = __phy_read(phydev, MII_MMD_DATA); +- phy_unlock_mdio_bus(phydev); +- +- return val > 0; +-} +- +-static int rtlgen_match_phy_device(struct phy_device *phydev) +-{ +- return phydev->phy_id == RTL_GENERIC_PHYID && +- !rtlgen_supports_2_5gbps(phydev); +-} +- +-static int rtl8226_match_phy_device(struct phy_device *phydev) +-{ +- return phydev->phy_id == RTL_GENERIC_PHYID && +- rtlgen_supports_2_5gbps(phydev) && +- rtlgen_supports_mmd(phydev); +-} +- +-static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id, +- bool is_c45) +-{ +- if (phydev->is_c45) +- return is_c45 && (id == phydev->c45_ids.device_ids[1]); +- else +- return !is_c45 && (id == phydev->phy_id); +-} +- +-static int rtl8221b_match_phy_device(struct phy_device *phydev) +-{ +- return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev); +-} +- +-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev) +-{ +- return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); +-} +- +-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev) +-{ +- return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true); +-} +- +-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev) +-{ +- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false); +-} +- +-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev) +-{ +- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true); +-} +- +-static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev) +-{ +- if (phydev->is_c45) +- return false; +- +- switch (phydev->phy_id) { +- case RTL_GENERIC_PHYID: +- case RTL_8221B: +- case RTL_8251B: +- case 0x001cc841: +- break; +- default: +- return false; +- } +- +- return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev); +-} +- +-static int rtl8251b_c45_match_phy_device(struct phy_device *phydev) +-{ +- return rtlgen_is_c45_match(phydev, RTL_8251B, true); +-} +- +-static int rtlgen_resume(struct phy_device *phydev) +-{ +- int ret = genphy_resume(phydev); +- +- /* Internal PHY's from RTL8168h up may not be instantly ready */ +- msleep(20); +- +- return ret; +-} +- +-static int rtlgen_c45_resume(struct phy_device *phydev) +-{ +- int ret = genphy_c45_pma_resume(phydev); +- +- msleep(20); +- +- return ret; +-} +- +-static int rtl9000a_config_init(struct phy_device *phydev) +-{ +- phydev->autoneg = AUTONEG_DISABLE; +- phydev->speed = SPEED_100; +- phydev->duplex = DUPLEX_FULL; +- +- return 0; +-} +- +-static int rtl9000a_config_aneg(struct phy_device *phydev) +-{ +- int ret; +- u16 ctl = 0; +- +- switch (phydev->master_slave_set) { +- case MASTER_SLAVE_CFG_MASTER_FORCE: +- ctl |= CTL1000_AS_MASTER; +- break; +- case MASTER_SLAVE_CFG_SLAVE_FORCE: +- break; +- case MASTER_SLAVE_CFG_UNKNOWN: +- case MASTER_SLAVE_CFG_UNSUPPORTED: +- return 0; +- default: +- phydev_warn(phydev, "Unsupported Master/Slave mode\n"); +- return -EOPNOTSUPP; +- } +- +- ret = phy_modify_changed(phydev, MII_CTRL1000, CTL1000_AS_MASTER, ctl); +- if (ret == 1) +- ret = genphy_soft_reset(phydev); +- +- return ret; +-} +- +-static int rtl9000a_read_status(struct phy_device *phydev) +-{ +- int ret; +- +- phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN; +- phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; +- +- ret = genphy_update_link(phydev); +- if (ret) +- return ret; +- +- ret = phy_read(phydev, MII_CTRL1000); +- if (ret < 0) +- return ret; +- if (ret & CTL1000_AS_MASTER) +- phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE; +- else +- phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE; +- +- ret = phy_read(phydev, MII_STAT1000); +- if (ret < 0) +- return ret; +- if (ret & LPA_1000MSRES) +- phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER; +- else +- phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE; +- +- return 0; +-} +- +-static int rtl9000a_ack_interrupt(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_read(phydev, RTL8211F_INSR); +- +- return (err < 0) ? err : 0; +-} +- +-static int rtl9000a_config_intr(struct phy_device *phydev) +-{ +- u16 val; +- int err; +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- err = rtl9000a_ack_interrupt(phydev); +- if (err) +- return err; +- +- val = (u16)~RTL9000A_GINMR_LINK_STATUS; +- err = phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val); +- } else { +- val = ~0; +- err = phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val); +- if (err) +- return err; +- +- err = rtl9000a_ack_interrupt(phydev); +- } +- +- return phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val); +-} +- +-static irqreturn_t rtl9000a_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status; +- +- irq_status = phy_read(phydev, RTL8211F_INSR); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- if (!(irq_status & RTL8211F_INER_LINK_STATUS)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- +-static struct phy_driver realtek_drvs[] = { +- { +- PHY_ID_MATCH_EXACT(0x00008201), +- .name = "RTL8201CP Ethernet", +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc816), +- .name = "RTL8201F Fast Ethernet", +- .config_intr = &rtl8201_config_intr, +- .handle_interrupt = rtl8201_handle_interrupt, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_MODEL(0x001cc880), +- .name = "RTL8208 Fast Ethernet", +- .read_mmd = genphy_read_mmd_unsupported, +- .write_mmd = genphy_write_mmd_unsupported, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc910), +- .name = "RTL8211 Gigabit Ethernet", +- .config_aneg = rtl8211_config_aneg, +- .read_mmd = &genphy_read_mmd_unsupported, +- .write_mmd = &genphy_write_mmd_unsupported, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc912), +- .name = "RTL8211B Gigabit Ethernet", +- .config_intr = &rtl8211b_config_intr, +- .handle_interrupt = rtl821x_handle_interrupt, +- .read_mmd = &genphy_read_mmd_unsupported, +- .write_mmd = &genphy_write_mmd_unsupported, +- .suspend = rtl8211b_suspend, +- .resume = rtl8211b_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc913), +- .name = "RTL8211C Gigabit Ethernet", +- .config_init = rtl8211c_config_init, +- .read_mmd = &genphy_read_mmd_unsupported, +- .write_mmd = &genphy_write_mmd_unsupported, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc914), +- .name = "RTL8211DN Gigabit Ethernet", +- .config_intr = rtl8211e_config_intr, +- .handle_interrupt = rtl821x_handle_interrupt, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc915), +- .name = "RTL8211E Gigabit Ethernet", +- .config_init = &rtl8211e_config_init, +- .config_intr = &rtl8211e_config_intr, +- .handle_interrupt = rtl821x_handle_interrupt, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc916), +- .name = "RTL8211F Gigabit Ethernet", +- .probe = rtl821x_probe, +- .config_init = &rtl8211f_config_init, +- .read_status = rtlgen_read_status, +- .config_intr = &rtl8211f_config_intr, +- .handle_interrupt = rtl8211f_handle_interrupt, +- .suspend = rtl821x_suspend, +- .resume = rtl821x_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- .flags = PHY_ALWAYS_CALL_SUSPEND, +- .led_hw_is_supported = rtl8211f_led_hw_is_supported, +- .led_hw_control_get = rtl8211f_led_hw_control_get, +- .led_hw_control_set = rtl8211f_led_hw_control_set, +- }, { +- PHY_ID_MATCH_EXACT(RTL_8211FVD_PHYID), +- .name = "RTL8211F-VD Gigabit Ethernet", +- .probe = rtl821x_probe, +- .config_init = &rtl8211f_config_init, +- .read_status = rtlgen_read_status, +- .config_intr = &rtl8211f_config_intr, +- .handle_interrupt = rtl8211f_handle_interrupt, +- .suspend = rtl821x_suspend, +- .resume = rtl821x_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- .flags = PHY_ALWAYS_CALL_SUSPEND, +- }, { +- .name = "Generic FE-GE Realtek PHY", +- .match_phy_device = rtlgen_match_phy_device, +- .read_status = rtlgen_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- .read_mmd = rtlgen_read_mmd, +- .write_mmd = rtlgen_write_mmd, +- }, { +- .name = "RTL8226 2.5Gbps PHY", +- .match_phy_device = rtl8226_match_phy_device, +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .read_status = rtl822x_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- .match_phy_device = rtl8221b_match_phy_device, +- .name = "RTL8226B_RTL8221B 2.5Gbps PHY", +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .config_init = rtl822xb_config_init, +- .get_rate_matching = rtl822xb_get_rate_matching, +- .read_status = rtl822xb_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc838), +- .name = "RTL8226-CG 2.5Gbps PHY", +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .read_status = rtl822x_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc848), +- .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .config_init = rtl822xb_config_init, +- .get_rate_matching = rtl822xb_get_rate_matching, +- .read_status = rtl822xb_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, +- .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .config_init = rtl822xb_config_init, +- .get_rate_matching = rtl822xb_get_rate_matching, +- .read_status = rtl822xb_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, +- .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", +- .config_init = rtl822xb_config_init, +- .get_rate_matching = rtl822xb_get_rate_matching, +- .get_features = rtl822x_c45_get_features, +- .config_aneg = rtl822x_c45_config_aneg, +- .read_status = rtl822xb_c45_read_status, +- .suspend = genphy_c45_pma_suspend, +- .resume = rtlgen_c45_resume, +- }, { +- .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device, +- .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .config_init = rtl822xb_config_init, +- .get_rate_matching = rtl822xb_get_rate_matching, +- .read_status = rtl822xb_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device, +- .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)", +- .config_init = rtl822xb_config_init, +- .get_rate_matching = rtl822xb_get_rate_matching, +- .get_features = rtl822x_c45_get_features, +- .config_aneg = rtl822x_c45_config_aneg, +- .read_status = rtl822xb_c45_read_status, +- .suspend = genphy_c45_pma_suspend, +- .resume = rtlgen_c45_resume, +- }, { +- .match_phy_device = rtl8251b_c45_match_phy_device, +- .name = "RTL8251B 5Gbps PHY", +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .read_status = rtl822x_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- .match_phy_device = rtl_internal_nbaset_match_phy_device, +- .name = "Realtek Internal NBASE-T PHY", +- .flags = PHY_IS_INTERNAL, +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .read_status = rtl822x_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- .read_mmd = rtl822x_read_mmd, +- .write_mmd = rtl822x_write_mmd, +- }, { +- PHY_ID_MATCH_EXACT(0x001ccad0), +- .name = "RTL8224 2.5Gbps PHY", +- .get_features = rtl822x_c45_get_features, +- .config_aneg = rtl822x_c45_config_aneg, +- .read_status = rtl822x_c45_read_status, +- .suspend = genphy_c45_pma_suspend, +- .resume = rtlgen_c45_resume, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc961), +- .name = "RTL8366RB Gigabit Ethernet", +- .config_init = &rtl8366rb_config_init, +- /* These interrupts are handled by the irq controller +- * embedded inside the RTL8366RB, they get unmasked when the +- * irq is requested and ACKed by reading the status register, +- * which is done by the irqchip code. +- */ +- .config_intr = genphy_no_config_intr, +- .handle_interrupt = genphy_handle_interrupt_no_ack, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- }, { +- PHY_ID_MATCH_EXACT(0x001ccb00), +- .name = "RTL9000AA_RTL9000AN Ethernet", +- .features = PHY_BASIC_T1_FEATURES, +- .config_init = rtl9000a_config_init, +- .config_aneg = rtl9000a_config_aneg, +- .read_status = rtl9000a_read_status, +- .config_intr = rtl9000a_config_intr, +- .handle_interrupt = rtl9000a_handle_interrupt, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc942), +- .name = "RTL8365MB-VC Gigabit Ethernet", +- /* Interrupt handling analogous to RTL8366RB */ +- .config_intr = genphy_no_config_intr, +- .handle_interrupt = genphy_handle_interrupt_no_ack, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- }, { +- PHY_ID_MATCH_EXACT(0x001cc960), +- .name = "RTL8366S Gigabit Ethernet", +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_mmd = genphy_read_mmd_unsupported, +- .write_mmd = genphy_write_mmd_unsupported, +- }, +-}; +- +-module_phy_driver(realtek_drvs); +- +-static const struct mdio_device_id __maybe_unused realtek_tbl[] = { +- { PHY_ID_MATCH_VENDOR(0x001cc800) }, +- { } +-}; +- +-MODULE_DEVICE_TABLE(mdio, realtek_tbl); +--- /dev/null ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -0,0 +1,1589 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* drivers/net/phy/realtek.c ++ * ++ * Driver for Realtek PHYs ++ * ++ * Author: Johnson Leung ++ * ++ * Copyright (c) 2004 Freescale Semiconductor, Inc. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RTL821x_PHYSR 0x11 ++#define RTL821x_PHYSR_DUPLEX BIT(13) ++#define RTL821x_PHYSR_SPEED GENMASK(15, 14) ++ ++#define RTL821x_INER 0x12 ++#define RTL8211B_INER_INIT 0x6400 ++#define RTL8211E_INER_LINK_STATUS BIT(10) ++#define RTL8211F_INER_LINK_STATUS BIT(4) ++ ++#define RTL821x_INSR 0x13 ++ ++#define RTL821x_EXT_PAGE_SELECT 0x1e ++#define RTL821x_PAGE_SELECT 0x1f ++ ++#define RTL8211F_PHYCR1 0x18 ++#define RTL8211F_PHYCR2 0x19 ++#define RTL8211F_INSR 0x1d ++ ++#define RTL8211F_LEDCR 0x10 ++#define RTL8211F_LEDCR_MODE BIT(15) ++#define RTL8211F_LEDCR_ACT_TXRX BIT(4) ++#define RTL8211F_LEDCR_LINK_1000 BIT(3) ++#define RTL8211F_LEDCR_LINK_100 BIT(1) ++#define RTL8211F_LEDCR_LINK_10 BIT(0) ++#define RTL8211F_LEDCR_MASK GENMASK(4, 0) ++#define RTL8211F_LEDCR_SHIFT 5 ++ ++#define RTL8211F_TX_DELAY BIT(8) ++#define RTL8211F_RX_DELAY BIT(3) ++ ++#define RTL8211F_ALDPS_PLL_OFF BIT(1) ++#define RTL8211F_ALDPS_ENABLE BIT(2) ++#define RTL8211F_ALDPS_XTAL_OFF BIT(12) ++ ++#define RTL8211E_CTRL_DELAY BIT(13) ++#define RTL8211E_TX_DELAY BIT(12) ++#define RTL8211E_RX_DELAY BIT(11) ++ ++#define RTL8211F_CLKOUT_EN BIT(0) ++ ++#define RTL8201F_ISR 0x1e ++#define RTL8201F_ISR_ANERR BIT(15) ++#define RTL8201F_ISR_DUPLEX BIT(13) ++#define RTL8201F_ISR_LINK BIT(11) ++#define RTL8201F_ISR_MASK (RTL8201F_ISR_ANERR | \ ++ RTL8201F_ISR_DUPLEX | \ ++ RTL8201F_ISR_LINK) ++#define RTL8201F_IER 0x13 ++ ++#define RTL822X_VND1_SERDES_OPTION 0x697a ++#define RTL822X_VND1_SERDES_OPTION_MODE_MASK GENMASK(5, 0) ++#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII 0 ++#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX 2 ++ ++#define RTL822X_VND1_SERDES_CTRL3 0x7580 ++#define RTL822X_VND1_SERDES_CTRL3_MODE_MASK GENMASK(5, 0) ++#define RTL822X_VND1_SERDES_CTRL3_MODE_SGMII 0x02 ++#define RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX 0x16 ++ ++/* RTL822X_VND2_XXXXX registers are only accessible when phydev->is_c45 ++ * is set, they cannot be accessed by C45-over-C22. ++ */ ++#define RTL822X_VND2_GBCR 0xa412 ++ ++#define RTL822X_VND2_GANLPAR 0xa414 ++ ++#define RTL8366RB_POWER_SAVE 0x15 ++#define RTL8366RB_POWER_SAVE_ON BIT(12) ++ ++#define RTL9000A_GINMR 0x14 ++#define RTL9000A_GINMR_LINK_STATUS BIT(4) ++ ++#define RTL_VND2_PHYSR 0xa434 ++#define RTL_VND2_PHYSR_DUPLEX BIT(3) ++#define RTL_VND2_PHYSR_SPEEDL GENMASK(5, 4) ++#define RTL_VND2_PHYSR_SPEEDH GENMASK(10, 9) ++#define RTL_VND2_PHYSR_MASTER BIT(11) ++#define RTL_VND2_PHYSR_SPEED_MASK (RTL_VND2_PHYSR_SPEEDL | RTL_VND2_PHYSR_SPEEDH) ++ ++#define RTL_GENERIC_PHYID 0x001cc800 ++#define RTL_8211FVD_PHYID 0x001cc878 ++#define RTL_8221B 0x001cc840 ++#define RTL_8221B_VB_CG 0x001cc849 ++#define RTL_8221B_VN_CG 0x001cc84a ++#define RTL_8251B 0x001cc862 ++ ++#define RTL8211F_LED_COUNT 3 ++ ++MODULE_DESCRIPTION("Realtek PHY driver"); ++MODULE_AUTHOR("Johnson Leung"); ++MODULE_LICENSE("GPL"); ++ ++struct rtl821x_priv { ++ u16 phycr1; ++ u16 phycr2; ++ bool has_phycr2; ++ struct clk *clk; ++}; ++ ++static int rtl821x_read_page(struct phy_device *phydev) ++{ ++ return __phy_read(phydev, RTL821x_PAGE_SELECT); ++} ++ ++static int rtl821x_write_page(struct phy_device *phydev, int page) ++{ ++ return __phy_write(phydev, RTL821x_PAGE_SELECT, page); ++} ++ ++static int rtl821x_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct rtl821x_priv *priv; ++ u32 phy_id = phydev->drv->phy_id; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->clk = devm_clk_get_optional_enabled(dev, NULL); ++ if (IS_ERR(priv->clk)) ++ return dev_err_probe(dev, PTR_ERR(priv->clk), ++ "failed to get phy clock\n"); ++ ++ ret = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR1); ++ if (ret < 0) ++ return ret; ++ ++ priv->phycr1 = ret & (RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF); ++ if (of_property_read_bool(dev->of_node, "realtek,aldps-enable")) ++ priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF; ++ ++ priv->has_phycr2 = !(phy_id == RTL_8211FVD_PHYID); ++ if (priv->has_phycr2) { ++ ret = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR2); ++ if (ret < 0) ++ return ret; ++ ++ priv->phycr2 = ret & RTL8211F_CLKOUT_EN; ++ if (of_property_read_bool(dev->of_node, "realtek,clkout-disable")) ++ priv->phycr2 &= ~RTL8211F_CLKOUT_EN; ++ } ++ ++ phydev->priv = priv; ++ ++ return 0; ++} ++ ++static int rtl8201_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read(phydev, RTL8201F_ISR); ++ ++ return (err < 0) ? err : 0; ++} ++ ++static int rtl821x_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read(phydev, RTL821x_INSR); ++ ++ return (err < 0) ? err : 0; ++} ++ ++static int rtl8211f_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read_paged(phydev, 0xa43, RTL8211F_INSR); ++ ++ return (err < 0) ? err : 0; ++} ++ ++static int rtl8201_config_intr(struct phy_device *phydev) ++{ ++ u16 val; ++ int err; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ err = rtl8201_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ val = BIT(13) | BIT(12) | BIT(11); ++ err = phy_write_paged(phydev, 0x7, RTL8201F_IER, val); ++ } else { ++ val = 0; ++ err = phy_write_paged(phydev, 0x7, RTL8201F_IER, val); ++ if (err) ++ return err; ++ ++ err = rtl8201_ack_interrupt(phydev); ++ } ++ ++ return err; ++} ++ ++static int rtl8211b_config_intr(struct phy_device *phydev) ++{ ++ int err; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ err = rtl821x_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ err = phy_write(phydev, RTL821x_INER, ++ RTL8211B_INER_INIT); ++ } else { ++ err = phy_write(phydev, RTL821x_INER, 0); ++ if (err) ++ return err; ++ ++ err = rtl821x_ack_interrupt(phydev); ++ } ++ ++ return err; ++} ++ ++static int rtl8211e_config_intr(struct phy_device *phydev) ++{ ++ int err; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ err = rtl821x_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ err = phy_write(phydev, RTL821x_INER, ++ RTL8211E_INER_LINK_STATUS); ++ } else { ++ err = phy_write(phydev, RTL821x_INER, 0); ++ if (err) ++ return err; ++ ++ err = rtl821x_ack_interrupt(phydev); ++ } ++ ++ return err; ++} ++ ++static int rtl8211f_config_intr(struct phy_device *phydev) ++{ ++ u16 val; ++ int err; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ err = rtl8211f_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ val = RTL8211F_INER_LINK_STATUS; ++ err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val); ++ } else { ++ val = 0; ++ err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val); ++ if (err) ++ return err; ++ ++ err = rtl8211f_ack_interrupt(phydev); ++ } ++ ++ return err; ++} ++ ++static irqreturn_t rtl8201_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_status; ++ ++ irq_status = phy_read(phydev, RTL8201F_ISR); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ if (!(irq_status & RTL8201F_ISR_MASK)) ++ return IRQ_NONE; ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t rtl821x_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_status, irq_enabled; ++ ++ irq_status = phy_read(phydev, RTL821x_INSR); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ irq_enabled = phy_read(phydev, RTL821x_INER); ++ if (irq_enabled < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ if (!(irq_status & irq_enabled)) ++ return IRQ_NONE; ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t rtl8211f_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_status; ++ ++ irq_status = phy_read_paged(phydev, 0xa43, RTL8211F_INSR); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ if (!(irq_status & RTL8211F_INER_LINK_STATUS)) ++ return IRQ_NONE; ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ ++static int rtl8211_config_aneg(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_config_aneg(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* Quirk was copied from vendor driver. Unfortunately it includes no ++ * description of the magic numbers. ++ */ ++ if (phydev->speed == SPEED_100 && phydev->autoneg == AUTONEG_DISABLE) { ++ phy_write(phydev, 0x17, 0x2138); ++ phy_write(phydev, 0x0e, 0x0260); ++ } else { ++ phy_write(phydev, 0x17, 0x2108); ++ phy_write(phydev, 0x0e, 0x0000); ++ } ++ ++ return 0; ++} ++ ++static int rtl8211c_config_init(struct phy_device *phydev) ++{ ++ /* RTL8211C has an issue when operating in Gigabit slave mode */ ++ return phy_set_bits(phydev, MII_CTRL1000, ++ CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER); ++} ++ ++static int rtl8211f_config_init(struct phy_device *phydev) ++{ ++ struct rtl821x_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ u16 val_txdly, val_rxdly; ++ int ret; ++ ++ ret = phy_modify_paged_changed(phydev, 0xa43, RTL8211F_PHYCR1, ++ RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF, ++ priv->phycr1); ++ if (ret < 0) { ++ dev_err(dev, "aldps mode configuration failed: %pe\n", ++ ERR_PTR(ret)); ++ return ret; ++ } ++ ++ switch (phydev->interface) { ++ case PHY_INTERFACE_MODE_RGMII: ++ val_txdly = 0; ++ val_rxdly = 0; ++ break; ++ ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ val_txdly = 0; ++ val_rxdly = RTL8211F_RX_DELAY; ++ break; ++ ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ val_txdly = RTL8211F_TX_DELAY; ++ val_rxdly = 0; ++ break; ++ ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ val_txdly = RTL8211F_TX_DELAY; ++ val_rxdly = RTL8211F_RX_DELAY; ++ break; ++ ++ default: /* the rest of the modes imply leaving delay as is. */ ++ return 0; ++ } ++ ++ ret = phy_modify_paged_changed(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, ++ val_txdly); ++ if (ret < 0) { ++ dev_err(dev, "Failed to update the TX delay register\n"); ++ return ret; ++ } else if (ret) { ++ dev_dbg(dev, ++ "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n", ++ val_txdly ? "Enabling" : "Disabling"); ++ } else { ++ dev_dbg(dev, ++ "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n", ++ val_txdly ? "enabled" : "disabled"); ++ } ++ ++ ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY, ++ val_rxdly); ++ if (ret < 0) { ++ dev_err(dev, "Failed to update the RX delay register\n"); ++ return ret; ++ } else if (ret) { ++ dev_dbg(dev, ++ "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n", ++ val_rxdly ? "Enabling" : "Disabling"); ++ } else { ++ dev_dbg(dev, ++ "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n", ++ val_rxdly ? "enabled" : "disabled"); ++ } ++ ++ if (priv->has_phycr2) { ++ ret = phy_modify_paged(phydev, 0xa43, RTL8211F_PHYCR2, ++ RTL8211F_CLKOUT_EN, priv->phycr2); ++ if (ret < 0) { ++ dev_err(dev, "clkout configuration failed: %pe\n", ++ ERR_PTR(ret)); ++ return ret; ++ } ++ ++ return genphy_soft_reset(phydev); ++ } ++ ++ return 0; ++} ++ ++static int rtl821x_suspend(struct phy_device *phydev) ++{ ++ struct rtl821x_priv *priv = phydev->priv; ++ int ret = 0; ++ ++ if (!phydev->wol_enabled) { ++ ret = genphy_suspend(phydev); ++ ++ if (ret) ++ return ret; ++ ++ clk_disable_unprepare(priv->clk); ++ } ++ ++ return ret; ++} ++ ++static int rtl821x_resume(struct phy_device *phydev) ++{ ++ struct rtl821x_priv *priv = phydev->priv; ++ int ret; ++ ++ if (!phydev->wol_enabled) ++ clk_prepare_enable(priv->clk); ++ ++ ret = genphy_resume(phydev); ++ if (ret < 0) ++ return ret; ++ ++ msleep(20); ++ ++ return 0; ++} ++ ++static int rtl8211f_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ const unsigned long mask = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX); ++ ++ /* The RTL8211F PHY supports these LED settings on up to three LEDs: ++ * - Link: Configurable subset of 10/100/1000 link rates ++ * - Active: Blink on activity, RX or TX is not differentiated ++ * The Active option has two modes, A and B: ++ * - A: Link and Active indication at configurable, but matching, ++ * subset of 10/100/1000 link rates ++ * - B: Link indication at configurable subset of 10/100/1000 link ++ * rates and Active indication always at all three 10+100+1000 ++ * link rates. ++ * This code currently uses mode B only. ++ */ ++ ++ if (index >= RTL8211F_LED_COUNT) ++ return -EINVAL; ++ ++ /* Filter out any other unsupported triggers. */ ++ if (rules & ~mask) ++ return -EOPNOTSUPP; ++ ++ /* RX and TX are not differentiated, either both are set or not set. */ ++ if (!(rules & BIT(TRIGGER_NETDEV_RX)) ^ !(rules & BIT(TRIGGER_NETDEV_TX))) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++ ++static int rtl8211f_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ int val; ++ ++ if (index >= RTL8211F_LED_COUNT) ++ return -EINVAL; ++ ++ val = phy_read_paged(phydev, 0xd04, RTL8211F_LEDCR); ++ if (val < 0) ++ return val; ++ ++ val >>= RTL8211F_LEDCR_SHIFT * index; ++ val &= RTL8211F_LEDCR_MASK; ++ ++ if (val & RTL8211F_LEDCR_LINK_10) ++ set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ ++ if (val & RTL8211F_LEDCR_LINK_100) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ ++ if (val & RTL8211F_LEDCR_LINK_1000) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ ++ if (val & RTL8211F_LEDCR_ACT_TXRX) { ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ } ++ ++ return 0; ++} ++ ++static int rtl8211f_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ const u16 mask = RTL8211F_LEDCR_MASK << (RTL8211F_LEDCR_SHIFT * index); ++ u16 reg = 0; ++ ++ if (index >= RTL8211F_LED_COUNT) ++ return -EINVAL; ++ ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ reg |= RTL8211F_LEDCR_LINK_10; ++ ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ reg |= RTL8211F_LEDCR_LINK_100; ++ ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ reg |= RTL8211F_LEDCR_LINK_1000; ++ ++ if (test_bit(TRIGGER_NETDEV_RX, &rules) || ++ test_bit(TRIGGER_NETDEV_TX, &rules)) { ++ reg |= RTL8211F_LEDCR_ACT_TXRX; ++ } ++ ++ reg <<= RTL8211F_LEDCR_SHIFT * index; ++ reg |= RTL8211F_LEDCR_MODE; /* Mode B */ ++ ++ return phy_modify_paged(phydev, 0xd04, RTL8211F_LEDCR, mask, reg); ++} ++ ++static int rtl8211e_config_init(struct phy_device *phydev) ++{ ++ int ret = 0, oldpage; ++ u16 val; ++ ++ /* enable TX/RX delay for rgmii-* modes, and disable them for rgmii. */ ++ switch (phydev->interface) { ++ case PHY_INTERFACE_MODE_RGMII: ++ val = RTL8211E_CTRL_DELAY | 0; ++ break; ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ val = RTL8211E_CTRL_DELAY | RTL8211E_TX_DELAY | RTL8211E_RX_DELAY; ++ break; ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ val = RTL8211E_CTRL_DELAY | RTL8211E_RX_DELAY; ++ break; ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ val = RTL8211E_CTRL_DELAY | RTL8211E_TX_DELAY; ++ break; ++ default: /* the rest of the modes imply leaving delays as is. */ ++ return 0; ++ } ++ ++ /* According to a sample driver there is a 0x1c config register on the ++ * 0xa4 extension page (0x7) layout. It can be used to disable/enable ++ * the RX/TX delays otherwise controlled by RXDLY/TXDLY pins. ++ * The configuration register definition: ++ * 14 = reserved ++ * 13 = Force Tx RX Delay controlled by bit12 bit11, ++ * 12 = RX Delay, 11 = TX Delay ++ * 10:0 = Test && debug settings reserved by realtek ++ */ ++ oldpage = phy_select_page(phydev, 0x7); ++ if (oldpage < 0) ++ goto err_restore_page; ++ ++ ret = __phy_write(phydev, RTL821x_EXT_PAGE_SELECT, 0xa4); ++ if (ret) ++ goto err_restore_page; ++ ++ ret = __phy_modify(phydev, 0x1c, RTL8211E_CTRL_DELAY ++ | RTL8211E_TX_DELAY | RTL8211E_RX_DELAY, ++ val); ++ ++err_restore_page: ++ return phy_restore_page(phydev, oldpage, ret); ++} ++ ++static int rtl8211b_suspend(struct phy_device *phydev) ++{ ++ phy_write(phydev, MII_MMD_DATA, BIT(9)); ++ ++ return genphy_suspend(phydev); ++} ++ ++static int rtl8211b_resume(struct phy_device *phydev) ++{ ++ phy_write(phydev, MII_MMD_DATA, 0); ++ ++ return genphy_resume(phydev); ++} ++ ++static int rtl8366rb_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = phy_set_bits(phydev, RTL8366RB_POWER_SAVE, ++ RTL8366RB_POWER_SAVE_ON); ++ if (ret) { ++ dev_err(&phydev->mdio.dev, ++ "error enabling power management\n"); ++ } ++ ++ return ret; ++} ++ ++/* get actual speed to cover the downshift case */ ++static void rtlgen_decode_physr(struct phy_device *phydev, int val) ++{ ++ /* bit 3 ++ * 0: Half Duplex ++ * 1: Full Duplex ++ */ ++ if (val & RTL_VND2_PHYSR_DUPLEX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ ++ switch (val & RTL_VND2_PHYSR_SPEED_MASK) { ++ case 0x0000: ++ phydev->speed = SPEED_10; ++ break; ++ case 0x0010: ++ phydev->speed = SPEED_100; ++ break; ++ case 0x0020: ++ phydev->speed = SPEED_1000; ++ break; ++ case 0x0200: ++ phydev->speed = SPEED_10000; ++ break; ++ case 0x0210: ++ phydev->speed = SPEED_2500; ++ break; ++ case 0x0220: ++ phydev->speed = SPEED_5000; ++ break; ++ default: ++ break; ++ } ++ ++ /* bit 11 ++ * 0: Slave Mode ++ * 1: Master Mode ++ */ ++ if (phydev->speed >= 1000) { ++ if (val & RTL_VND2_PHYSR_MASTER) ++ phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER; ++ else ++ phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE; ++ } else { ++ phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; ++ } ++} ++ ++static int rtlgen_read_status(struct phy_device *phydev) ++{ ++ int ret, val; ++ ++ ret = genphy_read_status(phydev); ++ if (ret < 0) ++ return ret; ++ ++ if (!phydev->link) ++ return 0; ++ ++ val = phy_read_paged(phydev, 0xa43, 0x12); ++ if (val < 0) ++ return val; ++ ++ rtlgen_decode_physr(phydev, val); ++ ++ return 0; ++} ++ ++static int rtlgen_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) ++{ ++ int ret; ++ ++ if (devnum == MDIO_MMD_VEND2) { ++ rtl821x_write_page(phydev, regnum >> 4); ++ ret = __phy_read(phydev, 0x10 + ((regnum & 0xf) >> 1)); ++ rtl821x_write_page(phydev, 0); ++ } else if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE) { ++ rtl821x_write_page(phydev, 0xa5c); ++ ret = __phy_read(phydev, 0x12); ++ rtl821x_write_page(phydev, 0); ++ } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) { ++ rtl821x_write_page(phydev, 0xa5d); ++ ret = __phy_read(phydev, 0x10); ++ rtl821x_write_page(phydev, 0); ++ } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE) { ++ rtl821x_write_page(phydev, 0xa5d); ++ ret = __phy_read(phydev, 0x11); ++ rtl821x_write_page(phydev, 0); ++ } else { ++ ret = -EOPNOTSUPP; ++ } ++ ++ return ret; ++} ++ ++static int rtlgen_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, ++ u16 val) ++{ ++ int ret; ++ ++ if (devnum == MDIO_MMD_VEND2) { ++ rtl821x_write_page(phydev, regnum >> 4); ++ ret = __phy_write(phydev, 0x10 + ((regnum & 0xf) >> 1), val); ++ rtl821x_write_page(phydev, 0); ++ } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) { ++ rtl821x_write_page(phydev, 0xa5d); ++ ret = __phy_write(phydev, 0x10, val); ++ rtl821x_write_page(phydev, 0); ++ } else { ++ ret = -EOPNOTSUPP; ++ } ++ ++ return ret; ++} ++ ++static int rtl822x_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) ++{ ++ int ret = rtlgen_read_mmd(phydev, devnum, regnum); ++ ++ if (ret != -EOPNOTSUPP) ++ return ret; ++ ++ if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE2) { ++ rtl821x_write_page(phydev, 0xa6e); ++ ret = __phy_read(phydev, 0x16); ++ rtl821x_write_page(phydev, 0); ++ } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV2) { ++ rtl821x_write_page(phydev, 0xa6d); ++ ret = __phy_read(phydev, 0x12); ++ rtl821x_write_page(phydev, 0); ++ } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE2) { ++ rtl821x_write_page(phydev, 0xa6d); ++ ret = __phy_read(phydev, 0x10); ++ rtl821x_write_page(phydev, 0); ++ } ++ ++ return ret; ++} ++ ++static int rtl822x_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, ++ u16 val) ++{ ++ int ret = rtlgen_write_mmd(phydev, devnum, regnum, val); ++ ++ if (ret != -EOPNOTSUPP) ++ return ret; ++ ++ if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV2) { ++ rtl821x_write_page(phydev, 0xa6d); ++ ret = __phy_write(phydev, 0x12, val); ++ rtl821x_write_page(phydev, 0); ++ } ++ ++ return ret; ++} ++ ++static int rtl822xb_config_init(struct phy_device *phydev) ++{ ++ bool has_2500, has_sgmii; ++ u16 mode; ++ int ret; ++ ++ has_2500 = test_bit(PHY_INTERFACE_MODE_2500BASEX, ++ phydev->host_interfaces) || ++ phydev->interface == PHY_INTERFACE_MODE_2500BASEX; ++ ++ has_sgmii = test_bit(PHY_INTERFACE_MODE_SGMII, ++ phydev->host_interfaces) || ++ phydev->interface == PHY_INTERFACE_MODE_SGMII; ++ ++ /* fill in possible interfaces */ ++ __assign_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->possible_interfaces, ++ has_2500); ++ __assign_bit(PHY_INTERFACE_MODE_SGMII, phydev->possible_interfaces, ++ has_sgmii); ++ ++ if (!has_2500 && !has_sgmii) ++ return 0; ++ ++ /* determine SerDes option mode */ ++ if (has_2500 && !has_sgmii) { ++ mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX; ++ phydev->rate_matching = RATE_MATCH_PAUSE; ++ } else { ++ mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII; ++ phydev->rate_matching = RATE_MATCH_NONE; ++ } ++ ++ /* the following sequence with magic numbers sets up the SerDes ++ * option mode ++ */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75f3, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1, ++ RTL822X_VND1_SERDES_OPTION, ++ RTL822X_VND1_SERDES_OPTION_MODE_MASK, ++ mode); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6a04, 0x0503); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f10, 0xd455); ++ if (ret < 0) ++ return ret; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020); ++} ++ ++static int rtl822xb_get_rate_matching(struct phy_device *phydev, ++ phy_interface_t iface) ++{ ++ int val; ++ ++ /* Only rate matching at 2500base-x */ ++ if (iface != PHY_INTERFACE_MODE_2500BASEX) ++ return RATE_MATCH_NONE; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_OPTION); ++ if (val < 0) ++ return val; ++ ++ if ((val & RTL822X_VND1_SERDES_OPTION_MODE_MASK) == ++ RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX) ++ return RATE_MATCH_PAUSE; ++ ++ /* RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII */ ++ return RATE_MATCH_NONE; ++} ++ ++static int rtl822x_get_features(struct phy_device *phydev) ++{ ++ int val; ++ ++ val = phy_read_paged(phydev, 0xa61, 0x13); ++ if (val < 0) ++ return val; ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ phydev->supported, val & MDIO_PMA_SPEED_2_5G); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, ++ phydev->supported, val & MDIO_PMA_SPEED_5G); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, ++ phydev->supported, val & MDIO_SPEED_10G); ++ ++ return genphy_read_abilities(phydev); ++} ++ ++static int rtl822x_config_aneg(struct phy_device *phydev) ++{ ++ int ret = 0; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE) { ++ u16 adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); ++ ++ ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12, ++ MDIO_AN_10GBT_CTRL_ADV2_5G | ++ MDIO_AN_10GBT_CTRL_ADV5G, ++ adv); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return __genphy_config_aneg(phydev, ret); ++} ++ ++static void rtl822xb_update_interface(struct phy_device *phydev) ++{ ++ int val; ++ ++ if (!phydev->link) ++ return; ++ ++ /* Change interface according to serdes mode */ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_CTRL3); ++ if (val < 0) ++ return; ++ ++ switch (val & RTL822X_VND1_SERDES_CTRL3_MODE_MASK) { ++ case RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX: ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ break; ++ case RTL822X_VND1_SERDES_CTRL3_MODE_SGMII: ++ phydev->interface = PHY_INTERFACE_MODE_SGMII; ++ break; ++ } ++} ++ ++static int rtl822x_read_status(struct phy_device *phydev) ++{ ++ int lpadv, ret; ++ ++ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0); ++ ++ ret = rtlgen_read_status(phydev); ++ if (ret < 0) ++ return ret; ++ ++ if (phydev->autoneg == AUTONEG_DISABLE || ++ !phydev->autoneg_complete) ++ return 0; ++ ++ lpadv = phy_read_paged(phydev, 0xa5d, 0x13); ++ if (lpadv < 0) ++ return lpadv; ++ ++ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv); ++ ++ return 0; ++} ++ ++static int rtl822xb_read_status(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = rtl822x_read_status(phydev); ++ if (ret < 0) ++ return ret; ++ ++ rtl822xb_update_interface(phydev); ++ ++ return 0; ++} ++ ++static int rtl822x_c45_get_features(struct phy_device *phydev) ++{ ++ linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, ++ phydev->supported); ++ ++ return genphy_c45_pma_read_abilities(phydev); ++} ++ ++static int rtl822x_c45_config_aneg(struct phy_device *phydev) ++{ ++ bool changed = false; ++ int ret, val; ++ ++ if (phydev->autoneg == AUTONEG_DISABLE) ++ return genphy_c45_pma_setup_forced(phydev); ++ ++ ret = genphy_c45_an_config_aneg(phydev); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ val = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); ++ ++ /* Vendor register as C45 has no standardized support for 1000BaseT */ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL822X_VND2_GBCR, ++ ADVERTISE_1000FULL, val); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ return genphy_c45_check_and_restart_aneg(phydev, changed); ++} ++ ++static int rtl822x_c45_read_status(struct phy_device *phydev) ++{ ++ int ret, val; ++ ++ /* Vendor register as C45 has no standardized support for 1000BaseT */ ++ if (phydev->autoneg == AUTONEG_ENABLE && genphy_c45_aneg_done(phydev)) { ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ RTL822X_VND2_GANLPAR); ++ if (val < 0) ++ return val; ++ } else { ++ val = 0; ++ } ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); ++ ++ ret = genphy_c45_read_status(phydev); ++ if (ret < 0) ++ return ret; ++ ++ if (!phydev->link) { ++ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; ++ return 0; ++ } ++ ++ /* Read actual speed from vendor register. */ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_VND2_PHYSR); ++ if (val < 0) ++ return val; ++ ++ rtlgen_decode_physr(phydev, val); ++ ++ return 0; ++} ++ ++static int rtl822xb_c45_read_status(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = rtl822x_c45_read_status(phydev); ++ if (ret < 0) ++ return ret; ++ ++ rtl822xb_update_interface(phydev); ++ ++ return 0; ++} ++ ++static bool rtlgen_supports_2_5gbps(struct phy_device *phydev) ++{ ++ int val; ++ ++ phy_write(phydev, RTL821x_PAGE_SELECT, 0xa61); ++ val = phy_read(phydev, 0x13); ++ phy_write(phydev, RTL821x_PAGE_SELECT, 0); ++ ++ return val >= 0 && val & MDIO_PMA_SPEED_2_5G; ++} ++ ++/* On internal PHY's MMD reads over C22 always return 0. ++ * Check a MMD register which is known to be non-zero. ++ */ ++static bool rtlgen_supports_mmd(struct phy_device *phydev) ++{ ++ int val; ++ ++ phy_lock_mdio_bus(phydev); ++ __phy_write(phydev, MII_MMD_CTRL, MDIO_MMD_PCS); ++ __phy_write(phydev, MII_MMD_DATA, MDIO_PCS_EEE_ABLE); ++ __phy_write(phydev, MII_MMD_CTRL, MDIO_MMD_PCS | MII_MMD_CTRL_NOINCR); ++ val = __phy_read(phydev, MII_MMD_DATA); ++ phy_unlock_mdio_bus(phydev); ++ ++ return val > 0; ++} ++ ++static int rtlgen_match_phy_device(struct phy_device *phydev) ++{ ++ return phydev->phy_id == RTL_GENERIC_PHYID && ++ !rtlgen_supports_2_5gbps(phydev); ++} ++ ++static int rtl8226_match_phy_device(struct phy_device *phydev) ++{ ++ return phydev->phy_id == RTL_GENERIC_PHYID && ++ rtlgen_supports_2_5gbps(phydev) && ++ rtlgen_supports_mmd(phydev); ++} ++ ++static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id, ++ bool is_c45) ++{ ++ if (phydev->is_c45) ++ return is_c45 && (id == phydev->c45_ids.device_ids[1]); ++ else ++ return !is_c45 && (id == phydev->phy_id); ++} ++ ++static int rtl8221b_match_phy_device(struct phy_device *phydev) ++{ ++ return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev); ++} ++ ++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev) ++{ ++ return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); ++} ++ ++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev) ++{ ++ return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true); ++} ++ ++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev) ++{ ++ return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false); ++} ++ ++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev) ++{ ++ return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true); ++} ++ ++static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev) ++{ ++ if (phydev->is_c45) ++ return false; ++ ++ switch (phydev->phy_id) { ++ case RTL_GENERIC_PHYID: ++ case RTL_8221B: ++ case RTL_8251B: ++ case 0x001cc841: ++ break; ++ default: ++ return false; ++ } ++ ++ return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev); ++} ++ ++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev) ++{ ++ return rtlgen_is_c45_match(phydev, RTL_8251B, true); ++} ++ ++static int rtlgen_resume(struct phy_device *phydev) ++{ ++ int ret = genphy_resume(phydev); ++ ++ /* Internal PHY's from RTL8168h up may not be instantly ready */ ++ msleep(20); ++ ++ return ret; ++} ++ ++static int rtlgen_c45_resume(struct phy_device *phydev) ++{ ++ int ret = genphy_c45_pma_resume(phydev); ++ ++ msleep(20); ++ ++ return ret; ++} ++ ++static int rtl9000a_config_init(struct phy_device *phydev) ++{ ++ phydev->autoneg = AUTONEG_DISABLE; ++ phydev->speed = SPEED_100; ++ phydev->duplex = DUPLEX_FULL; ++ ++ return 0; ++} ++ ++static int rtl9000a_config_aneg(struct phy_device *phydev) ++{ ++ int ret; ++ u16 ctl = 0; ++ ++ switch (phydev->master_slave_set) { ++ case MASTER_SLAVE_CFG_MASTER_FORCE: ++ ctl |= CTL1000_AS_MASTER; ++ break; ++ case MASTER_SLAVE_CFG_SLAVE_FORCE: ++ break; ++ case MASTER_SLAVE_CFG_UNKNOWN: ++ case MASTER_SLAVE_CFG_UNSUPPORTED: ++ return 0; ++ default: ++ phydev_warn(phydev, "Unsupported Master/Slave mode\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ ret = phy_modify_changed(phydev, MII_CTRL1000, CTL1000_AS_MASTER, ctl); ++ if (ret == 1) ++ ret = genphy_soft_reset(phydev); ++ ++ return ret; ++} ++ ++static int rtl9000a_read_status(struct phy_device *phydev) ++{ ++ int ret; ++ ++ phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN; ++ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; ++ ++ ret = genphy_update_link(phydev); ++ if (ret) ++ return ret; ++ ++ ret = phy_read(phydev, MII_CTRL1000); ++ if (ret < 0) ++ return ret; ++ if (ret & CTL1000_AS_MASTER) ++ phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE; ++ else ++ phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE; ++ ++ ret = phy_read(phydev, MII_STAT1000); ++ if (ret < 0) ++ return ret; ++ if (ret & LPA_1000MSRES) ++ phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER; ++ else ++ phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE; ++ ++ return 0; ++} ++ ++static int rtl9000a_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read(phydev, RTL8211F_INSR); ++ ++ return (err < 0) ? err : 0; ++} ++ ++static int rtl9000a_config_intr(struct phy_device *phydev) ++{ ++ u16 val; ++ int err; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ err = rtl9000a_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ val = (u16)~RTL9000A_GINMR_LINK_STATUS; ++ err = phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val); ++ } else { ++ val = ~0; ++ err = phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val); ++ if (err) ++ return err; ++ ++ err = rtl9000a_ack_interrupt(phydev); ++ } ++ ++ return phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val); ++} ++ ++static irqreturn_t rtl9000a_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_status; ++ ++ irq_status = phy_read(phydev, RTL8211F_INSR); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ if (!(irq_status & RTL8211F_INER_LINK_STATUS)) ++ return IRQ_NONE; ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct phy_driver realtek_drvs[] = { ++ { ++ PHY_ID_MATCH_EXACT(0x00008201), ++ .name = "RTL8201CP Ethernet", ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc816), ++ .name = "RTL8201F Fast Ethernet", ++ .config_intr = &rtl8201_config_intr, ++ .handle_interrupt = rtl8201_handle_interrupt, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_MODEL(0x001cc880), ++ .name = "RTL8208 Fast Ethernet", ++ .read_mmd = genphy_read_mmd_unsupported, ++ .write_mmd = genphy_write_mmd_unsupported, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc910), ++ .name = "RTL8211 Gigabit Ethernet", ++ .config_aneg = rtl8211_config_aneg, ++ .read_mmd = &genphy_read_mmd_unsupported, ++ .write_mmd = &genphy_write_mmd_unsupported, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc912), ++ .name = "RTL8211B Gigabit Ethernet", ++ .config_intr = &rtl8211b_config_intr, ++ .handle_interrupt = rtl821x_handle_interrupt, ++ .read_mmd = &genphy_read_mmd_unsupported, ++ .write_mmd = &genphy_write_mmd_unsupported, ++ .suspend = rtl8211b_suspend, ++ .resume = rtl8211b_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc913), ++ .name = "RTL8211C Gigabit Ethernet", ++ .config_init = rtl8211c_config_init, ++ .read_mmd = &genphy_read_mmd_unsupported, ++ .write_mmd = &genphy_write_mmd_unsupported, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc914), ++ .name = "RTL8211DN Gigabit Ethernet", ++ .config_intr = rtl8211e_config_intr, ++ .handle_interrupt = rtl821x_handle_interrupt, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc915), ++ .name = "RTL8211E Gigabit Ethernet", ++ .config_init = &rtl8211e_config_init, ++ .config_intr = &rtl8211e_config_intr, ++ .handle_interrupt = rtl821x_handle_interrupt, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc916), ++ .name = "RTL8211F Gigabit Ethernet", ++ .probe = rtl821x_probe, ++ .config_init = &rtl8211f_config_init, ++ .read_status = rtlgen_read_status, ++ .config_intr = &rtl8211f_config_intr, ++ .handle_interrupt = rtl8211f_handle_interrupt, ++ .suspend = rtl821x_suspend, ++ .resume = rtl821x_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ .flags = PHY_ALWAYS_CALL_SUSPEND, ++ .led_hw_is_supported = rtl8211f_led_hw_is_supported, ++ .led_hw_control_get = rtl8211f_led_hw_control_get, ++ .led_hw_control_set = rtl8211f_led_hw_control_set, ++ }, { ++ PHY_ID_MATCH_EXACT(RTL_8211FVD_PHYID), ++ .name = "RTL8211F-VD Gigabit Ethernet", ++ .probe = rtl821x_probe, ++ .config_init = &rtl8211f_config_init, ++ .read_status = rtlgen_read_status, ++ .config_intr = &rtl8211f_config_intr, ++ .handle_interrupt = rtl8211f_handle_interrupt, ++ .suspend = rtl821x_suspend, ++ .resume = rtl821x_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ .flags = PHY_ALWAYS_CALL_SUSPEND, ++ }, { ++ .name = "Generic FE-GE Realtek PHY", ++ .match_phy_device = rtlgen_match_phy_device, ++ .read_status = rtlgen_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ .read_mmd = rtlgen_read_mmd, ++ .write_mmd = rtlgen_write_mmd, ++ }, { ++ .name = "RTL8226 2.5Gbps PHY", ++ .match_phy_device = rtl8226_match_phy_device, ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .read_status = rtl822x_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ .match_phy_device = rtl8221b_match_phy_device, ++ .name = "RTL8226B_RTL8221B 2.5Gbps PHY", ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .config_init = rtl822xb_config_init, ++ .get_rate_matching = rtl822xb_get_rate_matching, ++ .read_status = rtl822xb_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc838), ++ .name = "RTL8226-CG 2.5Gbps PHY", ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .read_status = rtl822x_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc848), ++ .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .config_init = rtl822xb_config_init, ++ .get_rate_matching = rtl822xb_get_rate_matching, ++ .read_status = rtl822xb_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, ++ .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .config_init = rtl822xb_config_init, ++ .get_rate_matching = rtl822xb_get_rate_matching, ++ .read_status = rtl822xb_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, ++ .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", ++ .config_init = rtl822xb_config_init, ++ .get_rate_matching = rtl822xb_get_rate_matching, ++ .get_features = rtl822x_c45_get_features, ++ .config_aneg = rtl822x_c45_config_aneg, ++ .read_status = rtl822xb_c45_read_status, ++ .suspend = genphy_c45_pma_suspend, ++ .resume = rtlgen_c45_resume, ++ }, { ++ .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device, ++ .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .config_init = rtl822xb_config_init, ++ .get_rate_matching = rtl822xb_get_rate_matching, ++ .read_status = rtl822xb_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device, ++ .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)", ++ .config_init = rtl822xb_config_init, ++ .get_rate_matching = rtl822xb_get_rate_matching, ++ .get_features = rtl822x_c45_get_features, ++ .config_aneg = rtl822x_c45_config_aneg, ++ .read_status = rtl822xb_c45_read_status, ++ .suspend = genphy_c45_pma_suspend, ++ .resume = rtlgen_c45_resume, ++ }, { ++ .match_phy_device = rtl8251b_c45_match_phy_device, ++ .name = "RTL8251B 5Gbps PHY", ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .read_status = rtl822x_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ .match_phy_device = rtl_internal_nbaset_match_phy_device, ++ .name = "Realtek Internal NBASE-T PHY", ++ .flags = PHY_IS_INTERNAL, ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .read_status = rtl822x_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ .read_mmd = rtl822x_read_mmd, ++ .write_mmd = rtl822x_write_mmd, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001ccad0), ++ .name = "RTL8224 2.5Gbps PHY", ++ .get_features = rtl822x_c45_get_features, ++ .config_aneg = rtl822x_c45_config_aneg, ++ .read_status = rtl822x_c45_read_status, ++ .suspend = genphy_c45_pma_suspend, ++ .resume = rtlgen_c45_resume, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc961), ++ .name = "RTL8366RB Gigabit Ethernet", ++ .config_init = &rtl8366rb_config_init, ++ /* These interrupts are handled by the irq controller ++ * embedded inside the RTL8366RB, they get unmasked when the ++ * irq is requested and ACKed by reading the status register, ++ * which is done by the irqchip code. ++ */ ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001ccb00), ++ .name = "RTL9000AA_RTL9000AN Ethernet", ++ .features = PHY_BASIC_T1_FEATURES, ++ .config_init = rtl9000a_config_init, ++ .config_aneg = rtl9000a_config_aneg, ++ .read_status = rtl9000a_read_status, ++ .config_intr = rtl9000a_config_intr, ++ .handle_interrupt = rtl9000a_handle_interrupt, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc942), ++ .name = "RTL8365MB-VC Gigabit Ethernet", ++ /* Interrupt handling analogous to RTL8366RB */ ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ }, { ++ PHY_ID_MATCH_EXACT(0x001cc960), ++ .name = "RTL8366S Gigabit Ethernet", ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_mmd = genphy_read_mmd_unsupported, ++ .write_mmd = genphy_write_mmd_unsupported, ++ }, ++}; ++ ++module_phy_driver(realtek_drvs); ++ ++static const struct mdio_device_id __maybe_unused realtek_tbl[] = { ++ { PHY_ID_MATCH_VENDOR(0x001cc800) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, realtek_tbl); diff --git a/target/linux/generic/backport-6.12/781-11-v6.14-net-phy-realtek-add-hwmon-support-for-temp-sensor-on.patch b/target/linux/generic/backport-6.12/781-11-v6.14-net-phy-realtek-add-hwmon-support-for-temp-sensor-on.patch new file mode 100644 index 00000000000..2dec7010282 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-11-v6.14-net-phy-realtek-add-hwmon-support-for-temp-sensor-on.patch @@ -0,0 +1,173 @@ +From 33700ca45b7d2e1655d4cad95e25671e8a94e2f0 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Sat, 11 Jan 2025 21:51:24 +0100 +Subject: [PATCH] net: phy: realtek: add hwmon support for temp sensor on + RTL822x + +This adds hwmon support for the temperature sensor on RTL822x. +It's available on the standalone versions of the PHY's, and on +the integrated PHY's in RTL8125B/RTL8125D/RTL8126. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/ad6bfe9f-6375-4a00-84b4-bfb38a21bd71@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/Kconfig | 6 ++ + drivers/net/phy/realtek/Makefile | 1 + + drivers/net/phy/realtek/realtek.h | 10 ++++ + drivers/net/phy/realtek/realtek_hwmon.c | 79 +++++++++++++++++++++++++ + drivers/net/phy/realtek/realtek_main.c | 12 ++++ + 5 files changed, 108 insertions(+) + create mode 100644 drivers/net/phy/realtek/realtek.h + create mode 100644 drivers/net/phy/realtek/realtek_hwmon.c + +--- a/drivers/net/phy/realtek/Kconfig ++++ b/drivers/net/phy/realtek/Kconfig +@@ -3,3 +3,9 @@ config REALTEK_PHY + tristate "Realtek PHYs" + help + Currently supports RTL821x/RTL822x and fast ethernet PHYs ++ ++config REALTEK_PHY_HWMON ++ def_bool REALTEK_PHY && HWMON ++ depends on !(REALTEK_PHY=y && HWMON=m) ++ help ++ Optional hwmon support for the temperature sensor +--- a/drivers/net/phy/realtek/Makefile ++++ b/drivers/net/phy/realtek/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0 + realtek-y += realtek_main.o ++realtek-$(CONFIG_REALTEK_PHY_HWMON) += realtek_hwmon.o + obj-$(CONFIG_REALTEK_PHY) += realtek.o +--- /dev/null ++++ b/drivers/net/phy/realtek/realtek.h +@@ -0,0 +1,10 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef REALTEK_H ++#define REALTEK_H ++ ++#include ++ ++int rtl822x_hwmon_init(struct phy_device *phydev); ++ ++#endif /* REALTEK_H */ +--- /dev/null ++++ b/drivers/net/phy/realtek/realtek_hwmon.c +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * HWMON support for Realtek PHY's ++ * ++ * Author: Heiner Kallweit ++ */ ++ ++#include ++#include ++ ++#include "realtek.h" ++ ++#define RTL822X_VND2_TSALRM 0xa662 ++#define RTL822X_VND2_TSRR 0xbd84 ++#define RTL822X_VND2_TSSR 0xb54c ++ ++static int rtl822x_hwmon_get_temp(int raw) ++{ ++ if (raw >= 512) ++ raw -= 1024; ++ ++ return 1000 * raw / 2; ++} ++ ++static int rtl822x_hwmon_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ struct phy_device *phydev = dev_get_drvdata(dev); ++ int raw; ++ ++ switch (attr) { ++ case hwmon_temp_input: ++ raw = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_TSRR) & 0x3ff; ++ *val = rtl822x_hwmon_get_temp(raw); ++ break; ++ case hwmon_temp_max: ++ /* Chip reduces speed to 1G if threshold is exceeded */ ++ raw = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_TSSR) >> 6; ++ *val = rtl822x_hwmon_get_temp(raw); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct hwmon_ops rtl822x_hwmon_ops = { ++ .visible = 0444, ++ .read = rtl822x_hwmon_read, ++}; ++ ++static const struct hwmon_channel_info * const rtl822x_hwmon_info[] = { ++ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX), ++ NULL ++}; ++ ++static const struct hwmon_chip_info rtl822x_hwmon_chip_info = { ++ .ops = &rtl822x_hwmon_ops, ++ .info = rtl822x_hwmon_info, ++}; ++ ++int rtl822x_hwmon_init(struct phy_device *phydev) ++{ ++ struct device *hwdev, *dev = &phydev->mdio.dev; ++ const char *name; ++ ++ /* Ensure over-temp alarm is reset. */ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_TSALRM, 3); ++ ++ name = devm_hwmon_sanitize_name(dev, dev_name(dev)); ++ if (IS_ERR(name)) ++ return PTR_ERR(name); ++ ++ hwdev = devm_hwmon_device_register_with_info(dev, name, phydev, ++ &rtl822x_hwmon_chip_info, ++ NULL); ++ return PTR_ERR_OR_ZERO(hwdev); ++} +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -14,6 +14,8 @@ + #include + #include + ++#include "realtek.h" ++ + #define RTL821x_PHYSR 0x11 + #define RTL821x_PHYSR_DUPLEX BIT(13) + #define RTL821x_PHYSR_SPEED GENMASK(15, 14) +@@ -820,6 +822,15 @@ static int rtl822x_write_mmd(struct phy_ + return ret; + } + ++static int rtl822x_probe(struct phy_device *phydev) ++{ ++ if (IS_ENABLED(CONFIG_REALTEK_PHY_HWMON) && ++ phydev->phy_id != RTL_GENERIC_PHYID) ++ return rtl822x_hwmon_init(phydev); ++ ++ return 0; ++} ++ + static int rtl822xb_config_init(struct phy_device *phydev) + { + bool has_2500, has_sgmii; +@@ -1518,6 +1529,7 @@ static struct phy_driver realtek_drvs[] + .match_phy_device = rtl_internal_nbaset_match_phy_device, + .name = "Realtek Internal NBASE-T PHY", + .flags = PHY_IS_INTERNAL, ++ .probe = rtl822x_probe, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .read_status = rtl822x_read_status, diff --git a/target/linux/generic/backport-6.12/781-12-v6.14-net-phy-realtek-HWMON-support-for-standalone-version.patch b/target/linux/generic/backport-6.12/781-12-v6.14-net-phy-realtek-HWMON-support-for-standalone-version.patch new file mode 100644 index 00000000000..8b8c97c54f4 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-12-v6.14-net-phy-realtek-HWMON-support-for-standalone-version.patch @@ -0,0 +1,64 @@ +From 64ff63aeefb03139ae27454bd4208244579ae88e Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Fri, 17 Jan 2025 23:24:21 +0100 +Subject: [PATCH] net: phy: realtek: HWMON support for standalone versions of + RTL8221B and RTL8251 + +HWMON support has been added for the RTL8221/8251 PHYs integrated together +with the MAC inside the RTL8125/8126 chips. This patch extends temperature +reading support for standalone variants of the mentioned PHYs. + +I don't know whether the earlier revisions of the RTL8226 also have a +built-in temperature sensor, so they have been skipped for now. + +Tested on RTL8221B-VB-CG. + +Signed-off-by: Aleksander Jan Bajkowski +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/realtek/realtek_main.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1474,6 +1474,7 @@ static struct phy_driver realtek_drvs[] + }, { + .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, + .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", ++ .probe = rtl822x_probe, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, +@@ -1486,6 +1487,7 @@ static struct phy_driver realtek_drvs[] + }, { + .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, + .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", ++ .probe = rtl822x_probe, + .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, + .get_features = rtl822x_c45_get_features, +@@ -1496,6 +1498,7 @@ static struct phy_driver realtek_drvs[] + }, { + .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device, + .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", ++ .probe = rtl822x_probe, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, +@@ -1508,6 +1511,7 @@ static struct phy_driver realtek_drvs[] + }, { + .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device, + .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)", ++ .probe = rtl822x_probe, + .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, + .get_features = rtl822x_c45_get_features, +@@ -1518,6 +1522,7 @@ static struct phy_driver realtek_drvs[] + }, { + .match_phy_device = rtl8251b_c45_match_phy_device, + .name = "RTL8251B 5Gbps PHY", ++ .probe = rtl822x_probe, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .read_status = rtl822x_read_status, diff --git a/target/linux/generic/backport-6.12/781-13-v6.15-net-phy-realtek-make-HWMON-support-a-user-visible-Kc.patch b/target/linux/generic/backport-6.12/781-13-v6.15-net-phy-realtek-make-HWMON-support-a-user-visible-Kc.patch new file mode 100644 index 00000000000..821d7ee8798 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-13-v6.15-net-phy-realtek-make-HWMON-support-a-user-visible-Kc.patch @@ -0,0 +1,35 @@ +From 51773846fab24a353bed4ebb660997ced4bc32d7 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Mon, 3 Feb 2025 21:33:39 +0100 +Subject: [PATCH] net: phy: realtek: make HWMON support a user-visible Kconfig + symbol + +Make config symbol REALTEK_PHY_HWMON user-visible, so that users can +remove support if not needed. + +Suggested-by: Geert Uytterhoeven +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/3466ee92-166a-4b0f-9ae7-42b9e046f333@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/Kconfig | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/realtek/Kconfig ++++ b/drivers/net/phy/realtek/Kconfig +@@ -4,8 +4,12 @@ config REALTEK_PHY + help + Currently supports RTL821x/RTL822x and fast ethernet PHYs + ++if REALTEK_PHY ++ + config REALTEK_PHY_HWMON +- def_bool REALTEK_PHY && HWMON +- depends on !(REALTEK_PHY=y && HWMON=m) ++ bool "HWMON support for Realtek PHYs" ++ depends on HWMON && !(REALTEK_PHY=y && HWMON=m) + help + Optional hwmon support for the temperature sensor ++ ++endif # REALTEK_PHY diff --git a/target/linux/generic/backport-6.12/781-14-v6.15-net-phy-realtek-use-string-choices-helpers.patch b/target/linux/generic/backport-6.12/781-14-v6.15-net-phy-realtek-use-string-choices-helpers.patch new file mode 100644 index 00000000000..d3e09bc7cdd --- /dev/null +++ b/target/linux/generic/backport-6.12/781-14-v6.15-net-phy-realtek-use-string-choices-helpers.patch @@ -0,0 +1,54 @@ +From 0bea93fdbaf8675b7e8124bdcaf51497dcc8bcfa Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Mon, 3 Feb 2025 21:41:36 +0100 +Subject: [PATCH] net: phy: realtek: use string choices helpers + +Use string choices helpers to simplify the code. + +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202501190707.qQS8PGHW-lkp@intel.com/ +Signed-off-by: Heiner Kallweit +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/realtek/realtek_main.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #include "realtek.h" + +@@ -422,11 +423,11 @@ static int rtl8211f_config_init(struct p + } else if (ret) { + dev_dbg(dev, + "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n", +- val_txdly ? "Enabling" : "Disabling"); ++ str_enable_disable(val_txdly)); + } else { + dev_dbg(dev, + "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n", +- val_txdly ? "enabled" : "disabled"); ++ str_enabled_disabled(val_txdly)); + } + + ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY, +@@ -437,11 +438,11 @@ static int rtl8211f_config_init(struct p + } else if (ret) { + dev_dbg(dev, + "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n", +- val_rxdly ? "Enabling" : "Disabling"); ++ str_enable_disable(val_rxdly)); + } else { + dev_dbg(dev, + "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n", +- val_rxdly ? "enabled" : "disabled"); ++ str_enabled_disabled(val_rxdly)); + } + + if (priv->has_phycr2) { diff --git a/target/linux/generic/backport-6.12/781-15-v6.15-net-phy-realtek-improve-mmd-register-access-for-inte.patch b/target/linux/generic/backport-6.12/781-15-v6.15-net-phy-realtek-improve-mmd-register-access-for-inte.patch new file mode 100644 index 00000000000..3a3a20d58be --- /dev/null +++ b/target/linux/generic/backport-6.12/781-15-v6.15-net-phy-realtek-improve-mmd-register-access-for-inte.patch @@ -0,0 +1,134 @@ +From da681ed73fb980286fc29de707b35d76bb33e123 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Thu, 13 Feb 2025 20:18:17 +0100 +Subject: [PATCH] net: phy: realtek: improve mmd register access for internal + PHY's + +r8169 provides the MDIO bus for the internal PHY's. It has been extended +with c45 access functions for addressing MDIO_MMD_VEND2 registers. +So we can switch from paged access to directly addressing the +MDIO_MMD_VEND2 registers. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/a5f2333c-dda9-48ad-9801-77049766e632@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 79 +++++++++++--------------- + 1 file changed, 33 insertions(+), 46 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -735,29 +735,31 @@ static int rtlgen_read_status(struct phy + return 0; + } + ++static int rtlgen_read_vend2(struct phy_device *phydev, int regnum) ++{ ++ return __mdiobus_c45_read(phydev->mdio.bus, 0, MDIO_MMD_VEND2, regnum); ++} ++ ++static int rtlgen_write_vend2(struct phy_device *phydev, int regnum, u16 val) ++{ ++ return __mdiobus_c45_write(phydev->mdio.bus, 0, MDIO_MMD_VEND2, regnum, ++ val); ++} ++ + static int rtlgen_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) + { + int ret; + +- if (devnum == MDIO_MMD_VEND2) { +- rtl821x_write_page(phydev, regnum >> 4); +- ret = __phy_read(phydev, 0x10 + ((regnum & 0xf) >> 1)); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE) { +- rtl821x_write_page(phydev, 0xa5c); +- ret = __phy_read(phydev, 0x12); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) { +- rtl821x_write_page(phydev, 0xa5d); +- ret = __phy_read(phydev, 0x10); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE) { +- rtl821x_write_page(phydev, 0xa5d); +- ret = __phy_read(phydev, 0x11); +- rtl821x_write_page(phydev, 0); +- } else { ++ if (devnum == MDIO_MMD_VEND2) ++ ret = rtlgen_read_vend2(phydev, regnum); ++ else if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE) ++ ret = rtlgen_read_vend2(phydev, 0xa5c4); ++ else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) ++ ret = rtlgen_read_vend2(phydev, 0xa5d0); ++ else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE) ++ ret = rtlgen_read_vend2(phydev, 0xa5d2); ++ else + ret = -EOPNOTSUPP; +- } + + return ret; + } +@@ -767,17 +769,12 @@ static int rtlgen_write_mmd(struct phy_d + { + int ret; + +- if (devnum == MDIO_MMD_VEND2) { +- rtl821x_write_page(phydev, regnum >> 4); +- ret = __phy_write(phydev, 0x10 + ((regnum & 0xf) >> 1), val); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) { +- rtl821x_write_page(phydev, 0xa5d); +- ret = __phy_write(phydev, 0x10, val); +- rtl821x_write_page(phydev, 0); +- } else { ++ if (devnum == MDIO_MMD_VEND2) ++ ret = rtlgen_write_vend2(phydev, regnum, val); ++ else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) ++ ret = rtlgen_write_vend2(phydev, regnum, 0xa5d0); ++ else + ret = -EOPNOTSUPP; +- } + + return ret; + } +@@ -789,19 +786,12 @@ static int rtl822x_read_mmd(struct phy_d + if (ret != -EOPNOTSUPP) + return ret; + +- if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE2) { +- rtl821x_write_page(phydev, 0xa6e); +- ret = __phy_read(phydev, 0x16); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV2) { +- rtl821x_write_page(phydev, 0xa6d); +- ret = __phy_read(phydev, 0x12); +- rtl821x_write_page(phydev, 0); +- } else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE2) { +- rtl821x_write_page(phydev, 0xa6d); +- ret = __phy_read(phydev, 0x10); +- rtl821x_write_page(phydev, 0); +- } ++ if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE2) ++ ret = rtlgen_read_vend2(phydev, 0xa6ec); ++ else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV2) ++ ret = rtlgen_read_vend2(phydev, 0xa6d4); ++ else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE2) ++ ret = rtlgen_read_vend2(phydev, 0xa6d0); + + return ret; + } +@@ -814,11 +804,8 @@ static int rtl822x_write_mmd(struct phy_ + if (ret != -EOPNOTSUPP) + return ret; + +- if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV2) { +- rtl821x_write_page(phydev, 0xa6d); +- ret = __phy_write(phydev, 0x12, val); +- rtl821x_write_page(phydev, 0); +- } ++ if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV2) ++ ret = rtlgen_write_vend2(phydev, 0xa6d4, val); + + return ret; + } diff --git a/target/linux/generic/backport-6.12/781-16-v6.15-net-phy-realtek-switch-from-paged-to-MMD-ops-in-rtl8.patch b/target/linux/generic/backport-6.12/781-16-v6.15-net-phy-realtek-switch-from-paged-to-MMD-ops-in-rtl8.patch new file mode 100644 index 00000000000..5e3c3ce70a0 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-16-v6.15-net-phy-realtek-switch-from-paged-to-MMD-ops-in-rtl8.patch @@ -0,0 +1,52 @@ +From 02d3b306ac2f0b174753d1c5b9e4e5fb8ec5057e Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Thu, 13 Feb 2025 20:19:14 +0100 +Subject: [PATCH] net: phy: realtek: switch from paged to MMD ops in rtl822x + functions + +The MDIO bus provided by r8169 for the internal PHY's now supports +c45 ops for the MDIO_MMD_VEND2 device. So we can switch to standard +MMD ops here. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/81416f95-0fac-4225-87b4-828e3738b8ed@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -901,7 +901,7 @@ static int rtl822x_get_features(struct p + { + int val; + +- val = phy_read_paged(phydev, 0xa61, 0x13); ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xa616); + if (val < 0) + return val; + +@@ -922,10 +922,9 @@ static int rtl822x_config_aneg(struct ph + if (phydev->autoneg == AUTONEG_ENABLE) { + u16 adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); + +- ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12, +- MDIO_AN_10GBT_CTRL_ADV2_5G | +- MDIO_AN_10GBT_CTRL_ADV5G, +- adv); ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, 0xa5d4, ++ MDIO_AN_10GBT_CTRL_ADV2_5G | ++ MDIO_AN_10GBT_CTRL_ADV5G, adv); + if (ret < 0) + return ret; + } +@@ -969,7 +968,7 @@ static int rtl822x_read_status(struct ph + !phydev->autoneg_complete) + return 0; + +- lpadv = phy_read_paged(phydev, 0xa5d, 0x13); ++ lpadv = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xa5d6); + if (lpadv < 0) + return lpadv; + diff --git a/target/linux/generic/backport-6.12/781-17-v6.15-net-phy-realtek-add-helper-RTL822X_VND2_C22_REG.patch b/target/linux/generic/backport-6.12/781-17-v6.15-net-phy-realtek-add-helper-RTL822X_VND2_C22_REG.patch new file mode 100644 index 00000000000..5134d08b1cc --- /dev/null +++ b/target/linux/generic/backport-6.12/781-17-v6.15-net-phy-realtek-add-helper-RTL822X_VND2_C22_REG.patch @@ -0,0 +1,48 @@ +From 8af2136e77989a64fae0284bf76fd584e32edd3a Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Fri, 14 Feb 2025 21:31:14 +0100 +Subject: [PATCH] net: phy: realtek: add helper RTL822X_VND2_C22_REG + +C22 register space is mapped to 0xa400 in MMD VEND2 register space. +Add a helper to access mapped C22 registers. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/6344277b-c5c7-449b-ac89-d5425306ca76@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -79,9 +79,7 @@ + /* RTL822X_VND2_XXXXX registers are only accessible when phydev->is_c45 + * is set, they cannot be accessed by C45-over-C22. + */ +-#define RTL822X_VND2_GBCR 0xa412 +- +-#define RTL822X_VND2_GANLPAR 0xa414 ++#define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg)) + + #define RTL8366RB_POWER_SAVE 0x15 + #define RTL8366RB_POWER_SAVE_ON BIT(12) +@@ -1015,7 +1013,8 @@ static int rtl822x_c45_config_aneg(struc + val = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); + + /* Vendor register as C45 has no standardized support for 1000BaseT */ +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL822X_VND2_GBCR, ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, ++ RTL822X_VND2_C22_REG(MII_CTRL1000), + ADVERTISE_1000FULL, val); + if (ret < 0) + return ret; +@@ -1032,7 +1031,7 @@ static int rtl822x_c45_read_status(struc + /* Vendor register as C45 has no standardized support for 1000BaseT */ + if (phydev->autoneg == AUTONEG_ENABLE && genphy_c45_aneg_done(phydev)) { + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, +- RTL822X_VND2_GANLPAR); ++ RTL822X_VND2_C22_REG(MII_STAT1000)); + if (val < 0) + return val; + } else { diff --git a/target/linux/generic/backport-6.12/781-18-v6.15-net-phy-realtek-add-defines-for-shadowed-c45-standar.patch b/target/linux/generic/backport-6.12/781-18-v6.15-net-phy-realtek-add-defines-for-shadowed-c45-standar.patch new file mode 100644 index 00000000000..ff7d5b1fb6a --- /dev/null +++ b/target/linux/generic/backport-6.12/781-18-v6.15-net-phy-realtek-add-defines-for-shadowed-c45-standar.patch @@ -0,0 +1,113 @@ +From fabcfd6d10999024a721ae1b965b57eb8a305ace Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Sat, 15 Feb 2025 14:29:15 +0100 +Subject: [PATCH] net: phy: realtek: add defines for shadowed c45 standard + registers + +Realtek shadows standard c45 registers in VEND2 device register space. +Add defines for these VEND2 registers, based on the names of the +standard c45 registers. + +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/c90bdf76-f8b8-4d06-9656-7a52d5658ee6@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 33 +++++++++++++++++--------- + 1 file changed, 22 insertions(+), 11 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -94,6 +94,16 @@ + #define RTL_VND2_PHYSR_MASTER BIT(11) + #define RTL_VND2_PHYSR_SPEED_MASK (RTL_VND2_PHYSR_SPEEDL | RTL_VND2_PHYSR_SPEEDH) + ++#define RTL_MDIO_PCS_EEE_ABLE 0xa5c4 ++#define RTL_MDIO_AN_EEE_ADV 0xa5d0 ++#define RTL_MDIO_AN_EEE_LPABLE 0xa5d2 ++#define RTL_MDIO_AN_10GBT_CTRL 0xa5d4 ++#define RTL_MDIO_AN_10GBT_STAT 0xa5d6 ++#define RTL_MDIO_PMA_SPEED 0xa616 ++#define RTL_MDIO_AN_EEE_LPABLE2 0xa6d0 ++#define RTL_MDIO_AN_EEE_ADV2 0xa6d4 ++#define RTL_MDIO_PCS_EEE_ABLE2 0xa6ec ++ + #define RTL_GENERIC_PHYID 0x001cc800 + #define RTL_8211FVD_PHYID 0x001cc878 + #define RTL_8221B 0x001cc840 +@@ -751,11 +761,11 @@ static int rtlgen_read_mmd(struct phy_de + if (devnum == MDIO_MMD_VEND2) + ret = rtlgen_read_vend2(phydev, regnum); + else if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE) +- ret = rtlgen_read_vend2(phydev, 0xa5c4); ++ ret = rtlgen_read_vend2(phydev, RTL_MDIO_PCS_EEE_ABLE); + else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) +- ret = rtlgen_read_vend2(phydev, 0xa5d0); ++ ret = rtlgen_read_vend2(phydev, RTL_MDIO_AN_EEE_ADV); + else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE) +- ret = rtlgen_read_vend2(phydev, 0xa5d2); ++ ret = rtlgen_read_vend2(phydev, RTL_MDIO_AN_EEE_LPABLE); + else + ret = -EOPNOTSUPP; + +@@ -770,7 +780,7 @@ static int rtlgen_write_mmd(struct phy_d + if (devnum == MDIO_MMD_VEND2) + ret = rtlgen_write_vend2(phydev, regnum, val); + else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) +- ret = rtlgen_write_vend2(phydev, regnum, 0xa5d0); ++ ret = rtlgen_write_vend2(phydev, regnum, RTL_MDIO_AN_EEE_ADV); + else + ret = -EOPNOTSUPP; + +@@ -785,11 +795,11 @@ static int rtl822x_read_mmd(struct phy_d + return ret; + + if (devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE2) +- ret = rtlgen_read_vend2(phydev, 0xa6ec); ++ ret = rtlgen_read_vend2(phydev, RTL_MDIO_PCS_EEE_ABLE2); + else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV2) +- ret = rtlgen_read_vend2(phydev, 0xa6d4); ++ ret = rtlgen_read_vend2(phydev, RTL_MDIO_AN_EEE_ADV2); + else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE2) +- ret = rtlgen_read_vend2(phydev, 0xa6d0); ++ ret = rtlgen_read_vend2(phydev, RTL_MDIO_AN_EEE_LPABLE2); + + return ret; + } +@@ -803,7 +813,7 @@ static int rtl822x_write_mmd(struct phy_ + return ret; + + if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV2) +- ret = rtlgen_write_vend2(phydev, 0xa6d4, val); ++ ret = rtlgen_write_vend2(phydev, RTL_MDIO_AN_EEE_ADV2, val); + + return ret; + } +@@ -899,7 +909,7 @@ static int rtl822x_get_features(struct p + { + int val; + +- val = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xa616); ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_MDIO_PMA_SPEED); + if (val < 0) + return val; + +@@ -920,7 +930,8 @@ static int rtl822x_config_aneg(struct ph + if (phydev->autoneg == AUTONEG_ENABLE) { + u16 adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); + +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, 0xa5d4, ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, ++ RTL_MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV2_5G | + MDIO_AN_10GBT_CTRL_ADV5G, adv); + if (ret < 0) +@@ -966,7 +977,7 @@ static int rtl822x_read_status(struct ph + !phydev->autoneg_complete) + return 0; + +- lpadv = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xa5d6); ++ lpadv = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_MDIO_AN_10GBT_STAT); + if (lpadv < 0) + return lpadv; + diff --git a/target/linux/generic/backport-6.12/781-19-v6.15-net-phy-realtek-disable-PHY-mode-EEE.patch b/target/linux/generic/backport-6.12/781-19-v6.15-net-phy-realtek-disable-PHY-mode-EEE.patch new file mode 100644 index 00000000000..4d755d4e22a --- /dev/null +++ b/target/linux/generic/backport-6.12/781-19-v6.15-net-phy-realtek-disable-PHY-mode-EEE.patch @@ -0,0 +1,54 @@ +From bfc17c1658353f22843c7c13e27c2d31950f1887 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Sun, 16 Mar 2025 12:39:54 +0000 +Subject: [PATCH] net: phy: realtek: disable PHY-mode EEE + +Realtek RTL8211F has a "PHY-mode" EEE support which interferes with an +IEEE 802.3 compliant implementation. This mode defaults to enabled, and +results in the MAC receive path not seeing the link transition to LPI +state. + +Fix this by disabling PHY-mode EEE. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/E1ttnHW-00785s-Uq@rmk-PC.armlinux.org.uk +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -33,6 +33,9 @@ + + #define RTL8211F_PHYCR1 0x18 + #define RTL8211F_PHYCR2 0x19 ++#define RTL8211F_CLKOUT_EN BIT(0) ++#define RTL8211F_PHYCR2_PHY_EEE_ENABLE BIT(5) ++ + #define RTL8211F_INSR 0x1d + + #define RTL8211F_LEDCR 0x10 +@@ -55,8 +58,6 @@ + #define RTL8211E_TX_DELAY BIT(12) + #define RTL8211E_RX_DELAY BIT(11) + +-#define RTL8211F_CLKOUT_EN BIT(0) +- + #define RTL8201F_ISR 0x1e + #define RTL8201F_ISR_ANERR BIT(15) + #define RTL8201F_ISR_DUPLEX BIT(13) +@@ -453,6 +454,12 @@ static int rtl8211f_config_init(struct p + str_enabled_disabled(val_rxdly)); + } + ++ /* Disable PHY-mode EEE so LPI is passed to the MAC */ ++ ret = phy_modify_paged(phydev, 0xa43, RTL8211F_PHYCR2, ++ RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0); ++ if (ret) ++ return ret; ++ + if (priv->has_phycr2) { + ret = phy_modify_paged(phydev, 0xa43, RTL8211F_PHYCR2, + RTL8211F_CLKOUT_EN, priv->phycr2); diff --git a/target/linux/generic/backport-6.12/781-20-v6.16-net-phy-realtek-Add-support-for-WOL-magic-packet-on.patch b/target/linux/generic/backport-6.12/781-20-v6.16-net-phy-realtek-Add-support-for-WOL-magic-packet-on.patch new file mode 100644 index 00000000000..4ba06cc3dd2 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-20-v6.16-net-phy-realtek-Add-support-for-WOL-magic-packet-on.patch @@ -0,0 +1,126 @@ +From 7840e4d6f48a75413470935ebdc4bab4fc0c035e Mon Sep 17 00:00:00 2001 +From: Daniel Braunwarth +Date: Tue, 29 Apr 2025 13:33:37 +0200 +Subject: [PATCH] net: phy: realtek: Add support for WOL magic packet on + RTL8211F + +The RTL8211F supports multiple WOL modes. This patch adds support for +magic packets. + +The PHY notifies the system via the INTB/PMEB pin when a WOL event +occurs. + +Signed-off-by: Daniel Braunwarth +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250429-realtek_wol-v2-1-8f84def1ef2c@kuka.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 69 ++++++++++++++++++++++++++ + 1 file changed, 69 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -38,6 +39,24 @@ + + #define RTL8211F_INSR 0x1d + ++/* RTL8211F WOL interrupt configuration */ ++#define RTL8211F_INTBCR_PAGE 0xd40 ++#define RTL8211F_INTBCR 0x16 ++#define RTL8211F_INTBCR_INTB_PMEB BIT(5) ++ ++/* RTL8211F WOL settings */ ++#define RTL8211F_WOL_SETTINGS_PAGE 0xd8a ++#define RTL8211F_WOL_SETTINGS_EVENTS 16 ++#define RTL8211F_WOL_EVENT_MAGIC BIT(12) ++#define RTL8211F_WOL_SETTINGS_STATUS 17 ++#define RTL8211F_WOL_STATUS_RESET (BIT(15) | 0x1fff) ++ ++/* RTL8211F Unique phyiscal and multicast address (WOL) */ ++#define RTL8211F_PHYSICAL_ADDR_PAGE 0xd8c ++#define RTL8211F_PHYSICAL_ADDR_WORD0 16 ++#define RTL8211F_PHYSICAL_ADDR_WORD1 17 ++#define RTL8211F_PHYSICAL_ADDR_WORD2 18 ++ + #define RTL8211F_LEDCR 0x10 + #define RTL8211F_LEDCR_MODE BIT(15) + #define RTL8211F_LEDCR_ACT_TXRX BIT(4) +@@ -123,6 +142,7 @@ struct rtl821x_priv { + u16 phycr2; + bool has_phycr2; + struct clk *clk; ++ u32 saved_wolopts; + }; + + static int rtl821x_read_page(struct phy_device *phydev) +@@ -354,6 +374,53 @@ static irqreturn_t rtl8211f_handle_inter + return IRQ_HANDLED; + } + ++static void rtl8211f_get_wol(struct phy_device *dev, struct ethtool_wolinfo *wol) ++{ ++ wol->supported = WAKE_MAGIC; ++ if (phy_read_paged(dev, RTL8211F_WOL_SETTINGS_PAGE, RTL8211F_WOL_SETTINGS_EVENTS) ++ & RTL8211F_WOL_EVENT_MAGIC) ++ wol->wolopts = WAKE_MAGIC; ++} ++ ++static int rtl8211f_set_wol(struct phy_device *dev, struct ethtool_wolinfo *wol) ++{ ++ const u8 *mac_addr = dev->attached_dev->dev_addr; ++ int oldpage; ++ ++ oldpage = phy_save_page(dev); ++ if (oldpage < 0) ++ goto err; ++ ++ if (wol->wolopts & WAKE_MAGIC) { ++ /* Store the device address for the magic packet */ ++ rtl821x_write_page(dev, RTL8211F_PHYSICAL_ADDR_PAGE); ++ __phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD0, mac_addr[1] << 8 | (mac_addr[0])); ++ __phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD1, mac_addr[3] << 8 | (mac_addr[2])); ++ __phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD2, mac_addr[5] << 8 | (mac_addr[4])); ++ ++ /* Enable magic packet matching and reset WOL status */ ++ rtl821x_write_page(dev, RTL8211F_WOL_SETTINGS_PAGE); ++ __phy_write(dev, RTL8211F_WOL_SETTINGS_EVENTS, RTL8211F_WOL_EVENT_MAGIC); ++ __phy_write(dev, RTL8211F_WOL_SETTINGS_STATUS, RTL8211F_WOL_STATUS_RESET); ++ ++ /* Enable the WOL interrupt */ ++ rtl821x_write_page(dev, RTL8211F_INTBCR_PAGE); ++ __phy_set_bits(dev, RTL8211F_INTBCR, RTL8211F_INTBCR_INTB_PMEB); ++ } else { ++ /* Disable the WOL interrupt */ ++ rtl821x_write_page(dev, RTL8211F_INTBCR_PAGE); ++ __phy_clear_bits(dev, RTL8211F_INTBCR, RTL8211F_INTBCR_INTB_PMEB); ++ ++ /* Disable magic packet matching and reset WOL status */ ++ rtl821x_write_page(dev, RTL8211F_WOL_SETTINGS_PAGE); ++ __phy_write(dev, RTL8211F_WOL_SETTINGS_EVENTS, 0); ++ __phy_write(dev, RTL8211F_WOL_SETTINGS_STATUS, RTL8211F_WOL_STATUS_RESET); ++ } ++ ++err: ++ return phy_restore_page(dev, oldpage, 0); ++} ++ + static int rtl8211_config_aneg(struct phy_device *phydev) + { + int ret; +@@ -1400,6 +1467,8 @@ static struct phy_driver realtek_drvs[] + .read_status = rtlgen_read_status, + .config_intr = &rtl8211f_config_intr, + .handle_interrupt = rtl8211f_handle_interrupt, ++ .set_wol = rtl8211f_set_wol, ++ .get_wol = rtl8211f_get_wol, + .suspend = rtl821x_suspend, + .resume = rtl821x_resume, + .read_page = rtl821x_read_page, diff --git a/target/linux/generic/backport-6.12/781-21-v6.16-net-phy-realtek-remove-unsed-RTL821x_PHYSR-macros.patch b/target/linux/generic/backport-6.12/781-21-v6.16-net-phy-realtek-remove-unsed-RTL821x_PHYSR-macros.patch new file mode 100644 index 00000000000..e054937fdd2 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-21-v6.16-net-phy-realtek-remove-unsed-RTL821x_PHYSR-macros.patch @@ -0,0 +1,28 @@ +From f3b265358b911fe9e495619bdfa7797749474f95 Mon Sep 17 00:00:00 2001 +From: Michael Klein +Date: Sun, 4 May 2025 19:29:11 +0200 +Subject: [PATCH] net: phy: realtek: remove unsed RTL821x_PHYSR* macros + +These macros have there since the first revision but were never used, so +let's just remove them. + +Signed-off-by: Michael Klein +Link: https://patch.msgid.link/20250504172916.243185-2-michael@fossekall.de +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -18,10 +18,6 @@ + + #include "realtek.h" + +-#define RTL821x_PHYSR 0x11 +-#define RTL821x_PHYSR_DUPLEX BIT(13) +-#define RTL821x_PHYSR_SPEED GENMASK(15, 14) +- + #define RTL821x_INER 0x12 + #define RTL8211B_INER_INIT 0x6400 + #define RTL8211E_INER_LINK_STATUS BIT(10) diff --git a/target/linux/generic/backport-6.12/781-22-v6.16-net-phy-realtek-Clean-up-RTL821x-ExtPage-access.patch b/target/linux/generic/backport-6.12/781-22-v6.16-net-phy-realtek-Clean-up-RTL821x-ExtPage-access.patch new file mode 100644 index 00000000000..62bbf32f1a7 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-22-v6.16-net-phy-realtek-Clean-up-RTL821x-ExtPage-access.patch @@ -0,0 +1,95 @@ +From 7c6fa3ffd2650347b1d37f028e232e53d617c1af Mon Sep 17 00:00:00 2001 +From: Michael Klein +Date: Sun, 4 May 2025 19:29:12 +0200 +Subject: [PATCH] net: phy: realtek: Clean up RTL821x ExtPage access + +Factor out RTL8211E extension page access code to +rtl821x_modify_ext_page() and clean up rtl8211e_config_init() + +Signed-off-by: Michael Klein +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250504172916.243185-3-michael@fossekall.de +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 38 ++++++++++++++++---------- + 1 file changed, 23 insertions(+), 15 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -26,7 +26,9 @@ + #define RTL821x_INSR 0x13 + + #define RTL821x_EXT_PAGE_SELECT 0x1e ++ + #define RTL821x_PAGE_SELECT 0x1f ++#define RTL821x_SET_EXT_PAGE 0x07 + + #define RTL8211F_PHYCR1 0x18 + #define RTL8211F_PHYCR2 0x19 +@@ -69,9 +71,12 @@ + #define RTL8211F_ALDPS_ENABLE BIT(2) + #define RTL8211F_ALDPS_XTAL_OFF BIT(12) + ++#define RTL8211E_RGMII_EXT_PAGE 0xa4 ++#define RTL8211E_RGMII_DELAY 0x1c + #define RTL8211E_CTRL_DELAY BIT(13) + #define RTL8211E_TX_DELAY BIT(12) + #define RTL8211E_RX_DELAY BIT(11) ++#define RTL8211E_DELAY_MASK GENMASK(13, 11) + + #define RTL8201F_ISR 0x1e + #define RTL8201F_ISR_ANERR BIT(15) +@@ -151,6 +156,21 @@ static int rtl821x_write_page(struct phy + return __phy_write(phydev, RTL821x_PAGE_SELECT, page); + } + ++static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page, ++ u32 regnum, u16 mask, u16 set) ++{ ++ int oldpage, ret = 0; ++ ++ oldpage = phy_select_page(phydev, RTL821x_SET_EXT_PAGE); ++ if (oldpage >= 0) { ++ ret = __phy_write(phydev, RTL821x_EXT_PAGE_SELECT, ext_page); ++ if (ret == 0) ++ ret = __phy_modify(phydev, regnum, mask, set); ++ } ++ ++ return phy_restore_page(phydev, oldpage, ret); ++} ++ + static int rtl821x_probe(struct phy_device *phydev) + { + struct device *dev = &phydev->mdio.dev; +@@ -670,7 +690,6 @@ static int rtl8211f_led_hw_control_set(s + + static int rtl8211e_config_init(struct phy_device *phydev) + { +- int ret = 0, oldpage; + u16 val; + + /* enable TX/RX delay for rgmii-* modes, and disable them for rgmii. */ +@@ -700,20 +719,9 @@ static int rtl8211e_config_init(struct p + * 12 = RX Delay, 11 = TX Delay + * 10:0 = Test && debug settings reserved by realtek + */ +- oldpage = phy_select_page(phydev, 0x7); +- if (oldpage < 0) +- goto err_restore_page; +- +- ret = __phy_write(phydev, RTL821x_EXT_PAGE_SELECT, 0xa4); +- if (ret) +- goto err_restore_page; +- +- ret = __phy_modify(phydev, 0x1c, RTL8211E_CTRL_DELAY +- | RTL8211E_TX_DELAY | RTL8211E_RX_DELAY, +- val); +- +-err_restore_page: +- return phy_restore_page(phydev, oldpage, ret); ++ return rtl821x_modify_ext_page(phydev, RTL8211E_RGMII_EXT_PAGE, ++ RTL8211E_RGMII_DELAY, ++ RTL8211E_DELAY_MASK, val); + } + + static int rtl8211b_suspend(struct phy_device *phydev) diff --git a/target/linux/generic/backport-6.12/781-23-v6.16-net-phy-realtek-add-RTL8211F-register-defines.patch b/target/linux/generic/backport-6.12/781-23-v6.16-net-phy-realtek-add-RTL8211F-register-defines.patch new file mode 100644 index 00000000000..91169928322 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-23-v6.16-net-phy-realtek-add-RTL8211F-register-defines.patch @@ -0,0 +1,139 @@ +From 12d40df259e38851a0d973535e6023b33e2ea4f9 Mon Sep 17 00:00:00 2001 +From: Michael Klein +Date: Sun, 4 May 2025 19:29:13 +0200 +Subject: [PATCH] net: phy: realtek: add RTL8211F register defines + +Add some more defines for RTL8211F page and register numbers. + +Signed-off-by: Michael Klein +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250504172916.243185-4-michael@fossekall.de +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 34 ++++++++++++++++++-------- + 1 file changed, 24 insertions(+), 10 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -30,11 +30,14 @@ + #define RTL821x_PAGE_SELECT 0x1f + #define RTL821x_SET_EXT_PAGE 0x07 + ++/* RTL8211F PHY configuration */ ++#define RTL8211F_PHYCR_PAGE 0xa43 + #define RTL8211F_PHYCR1 0x18 + #define RTL8211F_PHYCR2 0x19 + #define RTL8211F_CLKOUT_EN BIT(0) + #define RTL8211F_PHYCR2_PHY_EEE_ENABLE BIT(5) + ++#define RTL8211F_INSR_PAGE 0xa43 + #define RTL8211F_INSR 0x1d + + /* RTL8211F WOL interrupt configuration */ +@@ -55,6 +58,8 @@ + #define RTL8211F_PHYSICAL_ADDR_WORD1 17 + #define RTL8211F_PHYSICAL_ADDR_WORD2 18 + ++/* RTL8211F LED configuration */ ++#define RTL8211F_LEDCR_PAGE 0xd04 + #define RTL8211F_LEDCR 0x10 + #define RTL8211F_LEDCR_MODE BIT(15) + #define RTL8211F_LEDCR_ACT_TXRX BIT(4) +@@ -64,7 +69,13 @@ + #define RTL8211F_LEDCR_MASK GENMASK(4, 0) + #define RTL8211F_LEDCR_SHIFT 5 + ++/* RTL8211F RGMII configuration */ ++#define RTL8211F_RGMII_PAGE 0xd08 ++ ++#define RTL8211F_TXCR 0x11 + #define RTL8211F_TX_DELAY BIT(8) ++ ++#define RTL8211F_RXCR 0x15 + #define RTL8211F_RX_DELAY BIT(3) + + #define RTL8211F_ALDPS_PLL_OFF BIT(1) +@@ -187,7 +198,7 @@ static int rtl821x_probe(struct phy_devi + return dev_err_probe(dev, PTR_ERR(priv->clk), + "failed to get phy clock\n"); + +- ret = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR1); ++ ret = phy_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1); + if (ret < 0) + return ret; + +@@ -197,7 +208,7 @@ static int rtl821x_probe(struct phy_devi + + priv->has_phycr2 = !(phy_id == RTL_8211FVD_PHYID); + if (priv->has_phycr2) { +- ret = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR2); ++ ret = phy_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2); + if (ret < 0) + return ret; + +@@ -233,7 +244,7 @@ static int rtl8211f_ack_interrupt(struct + { + int err; + +- err = phy_read_paged(phydev, 0xa43, RTL8211F_INSR); ++ err = phy_read_paged(phydev, RTL8211F_INSR_PAGE, RTL8211F_INSR); + + return (err < 0) ? err : 0; + } +@@ -376,7 +387,7 @@ static irqreturn_t rtl8211f_handle_inter + { + int irq_status; + +- irq_status = phy_read_paged(phydev, 0xa43, RTL8211F_INSR); ++ irq_status = phy_read_paged(phydev, RTL8211F_INSR_PAGE, RTL8211F_INSR); + if (irq_status < 0) { + phy_error(phydev); + return IRQ_NONE; +@@ -473,7 +484,7 @@ static int rtl8211f_config_init(struct p + u16 val_txdly, val_rxdly; + int ret; + +- ret = phy_modify_paged_changed(phydev, 0xa43, RTL8211F_PHYCR1, ++ ret = phy_modify_paged_changed(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1, + RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF, + priv->phycr1); + if (ret < 0) { +@@ -507,7 +518,8 @@ static int rtl8211f_config_init(struct p + return 0; + } + +- ret = phy_modify_paged_changed(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, ++ ret = phy_modify_paged_changed(phydev, RTL8211F_RGMII_PAGE, ++ RTL8211F_TXCR, RTL8211F_TX_DELAY, + val_txdly); + if (ret < 0) { + dev_err(dev, "Failed to update the TX delay register\n"); +@@ -522,7 +534,8 @@ static int rtl8211f_config_init(struct p + str_enabled_disabled(val_txdly)); + } + +- ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY, ++ ret = phy_modify_paged_changed(phydev, RTL8211F_RGMII_PAGE, ++ RTL8211F_RXCR, RTL8211F_RX_DELAY, + val_rxdly); + if (ret < 0) { + dev_err(dev, "Failed to update the RX delay register\n"); +@@ -538,14 +551,15 @@ static int rtl8211f_config_init(struct p + } + + /* Disable PHY-mode EEE so LPI is passed to the MAC */ +- ret = phy_modify_paged(phydev, 0xa43, RTL8211F_PHYCR2, ++ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2, + RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0); + if (ret) + return ret; + + if (priv->has_phycr2) { +- ret = phy_modify_paged(phydev, 0xa43, RTL8211F_PHYCR2, +- RTL8211F_CLKOUT_EN, priv->phycr2); ++ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, ++ RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, ++ priv->phycr2); + if (ret < 0) { + dev_err(dev, "clkout configuration failed: %pe\n", + ERR_PTR(ret)); diff --git a/target/linux/generic/backport-6.12/781-24-v6.16-net-phy-realtek-Group-RTL82-macro-definitions.patch b/target/linux/generic/backport-6.12/781-24-v6.16-net-phy-realtek-Group-RTL82-macro-definitions.patch new file mode 100644 index 00000000000..0319a2b109a --- /dev/null +++ b/target/linux/generic/backport-6.12/781-24-v6.16-net-phy-realtek-Group-RTL82-macro-definitions.patch @@ -0,0 +1,123 @@ +From 8c4d0172657c1f2d86b9c19172150abcd0e35c39 Mon Sep 17 00:00:00 2001 +From: Michael Klein +Date: Sun, 4 May 2025 19:29:14 +0200 +Subject: [PATCH] net: phy: realtek: Group RTL82* macro definitions + +Group macro definitions by PHY in lexicographic order. Within each PHY +block, definitions are order by page number and then register number. + +Signed-off-by: Michael Klein +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250504172916.243185-5-michael@fossekall.de +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 72 +++++++++++++------------- + 1 file changed, 37 insertions(+), 35 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -18,6 +18,16 @@ + + #include "realtek.h" + ++#define RTL8201F_IER 0x13 ++ ++#define RTL8201F_ISR 0x1e ++#define RTL8201F_ISR_ANERR BIT(15) ++#define RTL8201F_ISR_DUPLEX BIT(13) ++#define RTL8201F_ISR_LINK BIT(11) ++#define RTL8201F_ISR_MASK (RTL8201F_ISR_ANERR | \ ++ RTL8201F_ISR_DUPLEX | \ ++ RTL8201F_ISR_LINK) ++ + #define RTL821x_INER 0x12 + #define RTL8211B_INER_INIT 0x6400 + #define RTL8211E_INER_LINK_STATUS BIT(10) +@@ -30,9 +40,21 @@ + #define RTL821x_PAGE_SELECT 0x1f + #define RTL821x_SET_EXT_PAGE 0x07 + ++/* RTL8211E extension page 164/0xa4 */ ++#define RTL8211E_RGMII_EXT_PAGE 0xa4 ++#define RTL8211E_RGMII_DELAY 0x1c ++#define RTL8211E_CTRL_DELAY BIT(13) ++#define RTL8211E_TX_DELAY BIT(12) ++#define RTL8211E_RX_DELAY BIT(11) ++#define RTL8211E_DELAY_MASK GENMASK(13, 11) ++ + /* RTL8211F PHY configuration */ + #define RTL8211F_PHYCR_PAGE 0xa43 + #define RTL8211F_PHYCR1 0x18 ++#define RTL8211F_ALDPS_PLL_OFF BIT(1) ++#define RTL8211F_ALDPS_ENABLE BIT(2) ++#define RTL8211F_ALDPS_XTAL_OFF BIT(12) ++ + #define RTL8211F_PHYCR2 0x19 + #define RTL8211F_CLKOUT_EN BIT(0) + #define RTL8211F_PHYCR2_PHY_EEE_ENABLE BIT(5) +@@ -40,24 +62,6 @@ + #define RTL8211F_INSR_PAGE 0xa43 + #define RTL8211F_INSR 0x1d + +-/* RTL8211F WOL interrupt configuration */ +-#define RTL8211F_INTBCR_PAGE 0xd40 +-#define RTL8211F_INTBCR 0x16 +-#define RTL8211F_INTBCR_INTB_PMEB BIT(5) +- +-/* RTL8211F WOL settings */ +-#define RTL8211F_WOL_SETTINGS_PAGE 0xd8a +-#define RTL8211F_WOL_SETTINGS_EVENTS 16 +-#define RTL8211F_WOL_EVENT_MAGIC BIT(12) +-#define RTL8211F_WOL_SETTINGS_STATUS 17 +-#define RTL8211F_WOL_STATUS_RESET (BIT(15) | 0x1fff) +- +-/* RTL8211F Unique phyiscal and multicast address (WOL) */ +-#define RTL8211F_PHYSICAL_ADDR_PAGE 0xd8c +-#define RTL8211F_PHYSICAL_ADDR_WORD0 16 +-#define RTL8211F_PHYSICAL_ADDR_WORD1 17 +-#define RTL8211F_PHYSICAL_ADDR_WORD2 18 +- + /* RTL8211F LED configuration */ + #define RTL8211F_LEDCR_PAGE 0xd04 + #define RTL8211F_LEDCR 0x10 +@@ -78,25 +82,23 @@ + #define RTL8211F_RXCR 0x15 + #define RTL8211F_RX_DELAY BIT(3) + +-#define RTL8211F_ALDPS_PLL_OFF BIT(1) +-#define RTL8211F_ALDPS_ENABLE BIT(2) +-#define RTL8211F_ALDPS_XTAL_OFF BIT(12) ++/* RTL8211F WOL interrupt configuration */ ++#define RTL8211F_INTBCR_PAGE 0xd40 ++#define RTL8211F_INTBCR 0x16 ++#define RTL8211F_INTBCR_INTB_PMEB BIT(5) + +-#define RTL8211E_RGMII_EXT_PAGE 0xa4 +-#define RTL8211E_RGMII_DELAY 0x1c +-#define RTL8211E_CTRL_DELAY BIT(13) +-#define RTL8211E_TX_DELAY BIT(12) +-#define RTL8211E_RX_DELAY BIT(11) +-#define RTL8211E_DELAY_MASK GENMASK(13, 11) ++/* RTL8211F WOL settings */ ++#define RTL8211F_WOL_SETTINGS_PAGE 0xd8a ++#define RTL8211F_WOL_SETTINGS_EVENTS 16 ++#define RTL8211F_WOL_EVENT_MAGIC BIT(12) ++#define RTL8211F_WOL_SETTINGS_STATUS 17 ++#define RTL8211F_WOL_STATUS_RESET (BIT(15) | 0x1fff) + +-#define RTL8201F_ISR 0x1e +-#define RTL8201F_ISR_ANERR BIT(15) +-#define RTL8201F_ISR_DUPLEX BIT(13) +-#define RTL8201F_ISR_LINK BIT(11) +-#define RTL8201F_ISR_MASK (RTL8201F_ISR_ANERR | \ +- RTL8201F_ISR_DUPLEX | \ +- RTL8201F_ISR_LINK) +-#define RTL8201F_IER 0x13 ++/* RTL8211F Unique phyiscal and multicast address (WOL) */ ++#define RTL8211F_PHYSICAL_ADDR_PAGE 0xd8c ++#define RTL8211F_PHYSICAL_ADDR_WORD0 16 ++#define RTL8211F_PHYSICAL_ADDR_WORD1 17 ++#define RTL8211F_PHYSICAL_ADDR_WORD2 18 + + #define RTL822X_VND1_SERDES_OPTION 0x697a + #define RTL822X_VND1_SERDES_OPTION_MODE_MASK GENMASK(5, 0) diff --git a/target/linux/generic/backport-6.12/781-25-v6.16-net-phy-realtek-use-__set_bit-in-rtl8211f_led_hw_con.patch b/target/linux/generic/backport-6.12/781-25-v6.16-net-phy-realtek-use-__set_bit-in-rtl8211f_led_hw_con.patch new file mode 100644 index 00000000000..a9e3f9037d1 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-25-v6.16-net-phy-realtek-use-__set_bit-in-rtl8211f_led_hw_con.patch @@ -0,0 +1,42 @@ +From be1cc96ddf82bb0c0a159751f73239d6d3e9594a Mon Sep 17 00:00:00 2001 +From: Michael Klein +Date: Sun, 4 May 2025 19:29:15 +0200 +Subject: [PATCH] net: phy: realtek: use __set_bit() in + rtl8211f_led_hw_control_get() + +rtl8211f_led_hw_control_get() does not need atomic bit operations, +replace set_bit() by __set_bit(). + +Signed-off-by: Michael Klein +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250504172916.243185-6-michael@fossekall.de +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -659,17 +659,17 @@ static int rtl8211f_led_hw_control_get(s + val &= RTL8211F_LEDCR_MASK; + + if (val & RTL8211F_LEDCR_LINK_10) +- set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ __set_bit(TRIGGER_NETDEV_LINK_10, rules); + + if (val & RTL8211F_LEDCR_LINK_100) +- set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ __set_bit(TRIGGER_NETDEV_LINK_100, rules); + + if (val & RTL8211F_LEDCR_LINK_1000) +- set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ __set_bit(TRIGGER_NETDEV_LINK_1000, rules); + + if (val & RTL8211F_LEDCR_ACT_TXRX) { +- set_bit(TRIGGER_NETDEV_RX, rules); +- set_bit(TRIGGER_NETDEV_TX, rules); ++ __set_bit(TRIGGER_NETDEV_RX, rules); ++ __set_bit(TRIGGER_NETDEV_TX, rules); + } + + return 0; diff --git a/target/linux/generic/backport-6.12/781-26-v6.16-net-phy-realtek-Add-support-for-PHY-LEDs-on-RTL8211E.patch b/target/linux/generic/backport-6.12/781-26-v6.16-net-phy-realtek-Add-support-for-PHY-LEDs-on-RTL8211E.patch new file mode 100644 index 00000000000..a4b6450e92d --- /dev/null +++ b/target/linux/generic/backport-6.12/781-26-v6.16-net-phy-realtek-Add-support-for-PHY-LEDs-on-RTL8211E.patch @@ -0,0 +1,215 @@ +From 708686132ba02659267c0cebcc414348ece389a5 Mon Sep 17 00:00:00 2001 +From: Michael Klein +Date: Sun, 4 May 2025 19:29:16 +0200 +Subject: [PATCH] net: phy: realtek: Add support for PHY LEDs on RTL8211E + +Like the RTL8211F, the RTL8211E PHY supports up to three LEDs. +Add netdev trigger support for them, too. + +Signed-off-by: Michael Klein +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250504172916.243185-7-michael@fossekall.de +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 125 +++++++++++++++++++++++-- + 1 file changed, 119 insertions(+), 6 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -40,6 +40,20 @@ + #define RTL821x_PAGE_SELECT 0x1f + #define RTL821x_SET_EXT_PAGE 0x07 + ++/* RTL8211E extension page 44/0x2c */ ++#define RTL8211E_LEDCR_EXT_PAGE 0x2c ++#define RTL8211E_LEDCR1 0x1a ++#define RTL8211E_LEDCR1_ACT_TXRX BIT(4) ++#define RTL8211E_LEDCR1_MASK BIT(4) ++#define RTL8211E_LEDCR1_SHIFT 1 ++ ++#define RTL8211E_LEDCR2 0x1c ++#define RTL8211E_LEDCR2_LINK_1000 BIT(2) ++#define RTL8211E_LEDCR2_LINK_100 BIT(1) ++#define RTL8211E_LEDCR2_LINK_10 BIT(0) ++#define RTL8211E_LEDCR2_MASK GENMASK(2, 0) ++#define RTL8211E_LEDCR2_SHIFT 4 ++ + /* RTL8211E extension page 164/0xa4 */ + #define RTL8211E_RGMII_EXT_PAGE 0xa4 + #define RTL8211E_RGMII_DELAY 0x1c +@@ -145,7 +159,8 @@ + #define RTL_8221B_VN_CG 0x001cc84a + #define RTL_8251B 0x001cc862 + +-#define RTL8211F_LED_COUNT 3 ++/* RTL8211E and RTL8211F support up to three LEDs */ ++#define RTL8211x_LED_COUNT 3 + + MODULE_DESCRIPTION("Realtek PHY driver"); + MODULE_AUTHOR("Johnson Leung"); +@@ -169,6 +184,21 @@ static int rtl821x_write_page(struct phy + return __phy_write(phydev, RTL821x_PAGE_SELECT, page); + } + ++static int rtl821x_read_ext_page(struct phy_device *phydev, u16 ext_page, ++ u32 regnum) ++{ ++ int oldpage, ret = 0; ++ ++ oldpage = phy_select_page(phydev, RTL821x_SET_EXT_PAGE); ++ if (oldpage >= 0) { ++ ret = __phy_write(phydev, RTL821x_EXT_PAGE_SELECT, ext_page); ++ if (ret == 0) ++ ret = __phy_read(phydev, regnum); ++ } ++ ++ return phy_restore_page(phydev, oldpage, ret); ++} ++ + static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page, + u32 regnum, u16 mask, u16 set) + { +@@ -608,7 +638,7 @@ static int rtl821x_resume(struct phy_dev + return 0; + } + +-static int rtl8211f_led_hw_is_supported(struct phy_device *phydev, u8 index, ++static int rtl8211x_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) + { + const unsigned long mask = BIT(TRIGGER_NETDEV_LINK_10) | +@@ -627,9 +657,11 @@ static int rtl8211f_led_hw_is_supported( + * rates and Active indication always at all three 10+100+1000 + * link rates. + * This code currently uses mode B only. ++ * ++ * RTL8211E PHY LED has one mode, which works like RTL8211F mode B. + */ + +- if (index >= RTL8211F_LED_COUNT) ++ if (index >= RTL8211x_LED_COUNT) + return -EINVAL; + + /* Filter out any other unsupported triggers. */ +@@ -648,7 +680,7 @@ static int rtl8211f_led_hw_control_get(s + { + int val; + +- if (index >= RTL8211F_LED_COUNT) ++ if (index >= RTL8211x_LED_COUNT) + return -EINVAL; + + val = phy_read_paged(phydev, 0xd04, RTL8211F_LEDCR); +@@ -681,7 +713,7 @@ static int rtl8211f_led_hw_control_set(s + const u16 mask = RTL8211F_LEDCR_MASK << (RTL8211F_LEDCR_SHIFT * index); + u16 reg = 0; + +- if (index >= RTL8211F_LED_COUNT) ++ if (index >= RTL8211x_LED_COUNT) + return -EINVAL; + + if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) +@@ -704,6 +736,84 @@ static int rtl8211f_led_hw_control_set(s + return phy_modify_paged(phydev, 0xd04, RTL8211F_LEDCR, mask, reg); + } + ++static int rtl8211e_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ int ret; ++ u16 cr1, cr2; ++ ++ if (index >= RTL8211x_LED_COUNT) ++ return -EINVAL; ++ ++ ret = rtl821x_read_ext_page(phydev, RTL8211E_LEDCR_EXT_PAGE, ++ RTL8211E_LEDCR1); ++ if (ret < 0) ++ return ret; ++ ++ cr1 = ret >> RTL8211E_LEDCR1_SHIFT * index; ++ if (cr1 & RTL8211E_LEDCR1_ACT_TXRX) { ++ __set_bit(TRIGGER_NETDEV_RX, rules); ++ __set_bit(TRIGGER_NETDEV_TX, rules); ++ } ++ ++ ret = rtl821x_read_ext_page(phydev, RTL8211E_LEDCR_EXT_PAGE, ++ RTL8211E_LEDCR2); ++ if (ret < 0) ++ return ret; ++ ++ cr2 = ret >> RTL8211E_LEDCR2_SHIFT * index; ++ if (cr2 & RTL8211E_LEDCR2_LINK_10) ++ __set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ ++ if (cr2 & RTL8211E_LEDCR2_LINK_100) ++ __set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ ++ if (cr2 & RTL8211E_LEDCR2_LINK_1000) ++ __set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ ++ return ret; ++} ++ ++static int rtl8211e_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ const u16 cr1mask = ++ RTL8211E_LEDCR1_MASK << (RTL8211E_LEDCR1_SHIFT * index); ++ const u16 cr2mask = ++ RTL8211E_LEDCR2_MASK << (RTL8211E_LEDCR2_SHIFT * index); ++ u16 cr1 = 0, cr2 = 0; ++ int ret; ++ ++ if (index >= RTL8211x_LED_COUNT) ++ return -EINVAL; ++ ++ if (test_bit(TRIGGER_NETDEV_RX, &rules) || ++ test_bit(TRIGGER_NETDEV_TX, &rules)) { ++ cr1 |= RTL8211E_LEDCR1_ACT_TXRX; ++ } ++ ++ cr1 <<= RTL8211E_LEDCR1_SHIFT * index; ++ ret = rtl821x_modify_ext_page(phydev, RTL8211E_LEDCR_EXT_PAGE, ++ RTL8211E_LEDCR1, cr1mask, cr1); ++ if (ret < 0) ++ return ret; ++ ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ cr2 |= RTL8211E_LEDCR2_LINK_10; ++ ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ cr2 |= RTL8211E_LEDCR2_LINK_100; ++ ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ cr2 |= RTL8211E_LEDCR2_LINK_1000; ++ ++ cr2 <<= RTL8211E_LEDCR2_SHIFT * index; ++ ret = rtl821x_modify_ext_page(phydev, RTL8211E_LEDCR_EXT_PAGE, ++ RTL8211E_LEDCR2, cr2mask, cr2); ++ ++ return ret; ++} ++ + static int rtl8211e_config_init(struct phy_device *phydev) + { + u16 val; +@@ -1479,6 +1589,9 @@ static struct phy_driver realtek_drvs[] + .resume = genphy_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, ++ .led_hw_is_supported = rtl8211x_led_hw_is_supported, ++ .led_hw_control_get = rtl8211e_led_hw_control_get, ++ .led_hw_control_set = rtl8211e_led_hw_control_set, + }, { + PHY_ID_MATCH_EXACT(0x001cc916), + .name = "RTL8211F Gigabit Ethernet", +@@ -1494,7 +1607,7 @@ static struct phy_driver realtek_drvs[] + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + .flags = PHY_ALWAYS_CALL_SUSPEND, +- .led_hw_is_supported = rtl8211f_led_hw_is_supported, ++ .led_hw_is_supported = rtl8211x_led_hw_is_supported, + .led_hw_control_get = rtl8211f_led_hw_control_get, + .led_hw_control_set = rtl8211f_led_hw_control_set, + }, { diff --git a/target/linux/generic/backport-6.12/781-27-v6.16-net-phy-realtek-add-RTL8127-internal-PHY.patch b/target/linux/generic/backport-6.12/781-27-v6.16-net-phy-realtek-add-RTL8127-internal-PHY.patch new file mode 100644 index 00000000000..04c631dfe58 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-27-v6.16-net-phy-realtek-add-RTL8127-internal-PHY.patch @@ -0,0 +1,35 @@ +From 83d9623161283f5f6883ee3fdb88ef2177b8a5f1 Mon Sep 17 00:00:00 2001 +From: ChunHao Lin +Date: Fri, 16 May 2025 13:56:22 +0800 +Subject: [PATCH] net: phy: realtek: add RTL8127-internal PHY + +RTL8127-internal PHY is RTL8261C which is a integrated 10Gbps PHY with ID +0x001cc890. It follows the code path of RTL8125/RTL8126 internal NBase-T +PHY. + +Signed-off-by: ChunHao Lin +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250516055622.3772-1-hau@realtek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -158,6 +158,7 @@ + #define RTL_8221B_VB_CG 0x001cc849 + #define RTL_8221B_VN_CG 0x001cc84a + #define RTL_8251B 0x001cc862 ++#define RTL_8261C 0x001cc890 + + /* RTL8211E and RTL8211F support up to three LEDs */ + #define RTL8211x_LED_COUNT 3 +@@ -1370,6 +1371,7 @@ static int rtl_internal_nbaset_match_phy + case RTL_GENERIC_PHYID: + case RTL_8221B: + case RTL_8251B: ++ case RTL_8261C: + case 0x001cc841: + break; + default: diff --git a/target/linux/generic/backport-6.12/781-28-v6.18-net-phy-realtek-convert-RTL8226-CG-to-c45-only.patch b/target/linux/generic/backport-6.12/781-28-v6.18-net-phy-realtek-convert-RTL8226-CG-to-c45-only.patch new file mode 100644 index 00000000000..43f2c7b82ce --- /dev/null +++ b/target/linux/generic/backport-6.12/781-28-v6.18-net-phy-realtek-convert-RTL8226-CG-to-c45-only.patch @@ -0,0 +1,169 @@ +From 34167f1a024d2c5abae0b0325a6d0b8257160f86 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Wed, 13 Aug 2025 01:44:07 -0400 +Subject: net: phy: realtek: convert RTL8226-CG to c45 only + +Short: Convert the RTL8226-CG to c45 so it can be used in its +Realtek based ecosystems. + +Long: The RTL8226-CG can be mainly found on devices of the +Realtek Otto switch platform. Devices like the Zyxel XGS1210-12 +are based on it. These implement a hardware based phy polling +in the background to update SoC status registers. + +The hardware provides 4 smi busses where phys are attached to. +For each bus one can decide if it is polled in c45 or c22 mode. +See https://svanheule.net/realtek/longan/register/smi_glb_ctrl +With this setting the register access will be limited by the +hardware. This is very complex (including caching and special +c45-over-c22 handling). But basically it boils down to "enable +protocol x and SoC will disable register access via protocol y". + +Mainline already gained support for the rtl9300 mdio driver +in commit 24e31e474769 ("net: mdio: Add RTL9300 MDIO driver"). + +It covers the basic features, but a lot effort is still needed +to understand hardware properly. So it runs a simple setup by +selecting the proper bus mode during startup. + + /* Put the interfaces into C45 mode if required */ + glb_ctrl_mask = GENMASK(19, 16); + for (i = 0; i < MAX_SMI_BUSSES; i++) + if (priv->smi_bus_is_c45[i]) + glb_ctrl_val |= GLB_CTRL_INTF_SEL(i); + ... + err = regmap_update_bits(regmap, SMI_GLB_CTRL, + glb_ctrl_mask, glb_ctrl_val); + +To avoid complex coding later on, it limits access by only +providing either c22 or c45: + + bus->name = "Realtek Switch MDIO Bus"; + if (priv->smi_bus_is_c45[mdio_bus]) { + bus->read_c45 = rtl9300_mdio_read_c45; + bus->write_c45 = rtl9300_mdio_write_c45; + } else { + bus->read = rtl9300_mdio_read_c22; + bus->write = rtl9300_mdio_write_c22; + } + +Because of these limitations the existing RTL8226 phy driver +is not working at all on Realtek switches. Convert the driver +to c45-only. + +Luckily the RTL8226 seems to support proper MDIO_PMA_EXTABLE +flags. So standard function genphy_c45_pma_read_abilities() can +call genphy_c45_pma_read_ext_abilities() and 10/100/1000 is +populated right. Thus conversion is straight forward. + +Outputs before - REMARK: For this a "hacked" bus was used that +toggles the mode for each c22/c45 access. But that is slow and +produces unstable data in the SoC status registers). + +Settings for lan9: + Supported ports: [ TP MII ] + Supported link modes: 10baseT/Half 10baseT/Full + 100baseT/Half 100baseT/Full + 1000baseT/Full + 2500baseT/Full + Supported pause frame use: Symmetric Receive-only + Supports auto-negotiation: Yes + Supported FEC modes: Not reported + Advertised link modes: 10baseT/Half 10baseT/Full + 100baseT/Half 100baseT/Full + 1000baseT/Full + 2500baseT/Full + Advertised pause frame use: Symmetric Receive-only + Advertised auto-negotiation: Yes + Advertised FEC modes: Not reported + Speed: Unknown! + Duplex: Unknown! (255) + Port: Twisted Pair + PHYAD: 24 + Transceiver: external + Auto-negotiation: on + MDI-X: Unknown + Supports Wake-on: d + Wake-on: d + Link detected: no + +Outputs with this commit: + +Settings for lan9: + Supported ports: [ TP ] + Supported link modes: 10baseT/Half 10baseT/Full + 100baseT/Half 100baseT/Full + 1000baseT/Full + 2500baseT/Full + Supported pause frame use: Symmetric Receive-only + Supports auto-negotiation: Yes + Supported FEC modes: Not reported + Advertised link modes: 10baseT/Half 10baseT/Full + 100baseT/Half 100baseT/Full + 1000baseT/Full + 2500baseT/Full + Advertised pause frame use: Symmetric Receive-only + Advertised auto-negotiation: Yes + Advertised FEC modes: Not reported + Speed: Unknown! + Duplex: Unknown! (255) + Port: Twisted Pair + PHYAD: 24 + Transceiver: external + Auto-negotiation: on + MDI-X: Unknown + Supports Wake-on: d + Wake-on: d + Link detected: no + +Signed-off-by: Markus Stockhausen +Link: https://patch.msgid.link/20250813054407.1108285-1-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 28 +++++++++++++++++++++------- + 1 file changed, 21 insertions(+), 7 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1274,6 +1274,21 @@ static int rtl822x_c45_read_status(struc + return 0; + } + ++static int rtl822x_c45_soft_reset(struct phy_device *phydev) ++{ ++ int ret, val; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, ++ MDIO_CTRL1_RESET, MDIO_CTRL1_RESET); ++ if (ret < 0) ++ return ret; ++ ++ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PMAPMD, ++ MDIO_CTRL1, val, ++ !(val & MDIO_CTRL1_RESET), ++ 5000, 100000, true); ++} ++ + static int rtl822xb_c45_read_status(struct phy_device *phydev) + { + int ret; +@@ -1660,13 +1675,12 @@ static struct phy_driver realtek_drvs[] + }, { + PHY_ID_MATCH_EXACT(0x001cc838), + .name = "RTL8226-CG 2.5Gbps PHY", +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .read_status = rtl822x_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, ++ .soft_reset = rtl822x_c45_soft_reset, ++ .get_features = rtl822x_c45_get_features, ++ .config_aneg = rtl822x_c45_config_aneg, ++ .read_status = rtl822x_c45_read_status, ++ .suspend = genphy_c45_pma_suspend, ++ .resume = rtlgen_c45_resume, + }, { + PHY_ID_MATCH_EXACT(0x001cc848), + .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", diff --git a/target/linux/generic/backport-6.12/781-29-v6.18-net-phy-realtek-enable-serdes-option-mode-for-RTL8226-CG.patch b/target/linux/generic/backport-6.12/781-29-v6.18-net-phy-realtek-enable-serdes-option-mode-for-RTL8226-CG.patch new file mode 100644 index 00000000000..9ffb260df15 --- /dev/null +++ b/target/linux/generic/backport-6.12/781-29-v6.18-net-phy-realtek-enable-serdes-option-mode-for-RTL8226-CG.patch @@ -0,0 +1,92 @@ +From 3a752e67800106a5c42d802d67e06c60aa71d07b Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Fri, 15 Aug 2025 04:20:09 -0400 +Subject: net: phy: realtek: enable serdes option mode for RTL8226-CG + +The RTL8226-CG can make use of the serdes option mode feature to +dynamically switch between SGMII and 2500base-X. From what is +known the setup sequence is much simpler with no magic values. + +Convert the exiting config_init() into a helper that configures +the PHY depending on generation 1 or 2. Call the helper from two +separated new config_init() functions. + +Finally convert the phy_driver specs of the RTL8226-CG to make +use of the new configuration and switch over to the extended +read_status() function to dynamically change the interface +according to the serdes mode. + +Remark! The logic could be simpler if the serdes mode could be +set before all other generation 2 magic values. Due to missing +RTL8221B test hardware the mmd command order was kept. + +Tested on Zyxel XGS1210-12. + +Signed-off-by: Markus Stockhausen +Link: https://patch.msgid.link/20250815082009.3678865-1-markus.stockhausen@gmx.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 26 ++++++++++++++++++++------ + 1 file changed, 20 insertions(+), 6 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1032,7 +1032,7 @@ static int rtl822x_probe(struct phy_devi + return 0; + } + +-static int rtl822xb_config_init(struct phy_device *phydev) ++static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1) + { + bool has_2500, has_sgmii; + u16 mode; +@@ -1067,15 +1067,18 @@ static int rtl822xb_config_init(struct p + /* the following sequence with magic numbers sets up the SerDes + * option mode + */ +- ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75f3, 0); +- if (ret < 0) +- return ret; ++ ++ if (!gen1) { ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75f3, 0); ++ if (ret < 0) ++ return ret; ++ } + + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1, + RTL822X_VND1_SERDES_OPTION, + RTL822X_VND1_SERDES_OPTION_MODE_MASK, + mode); +- if (ret < 0) ++ if (gen1 || ret < 0) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6a04, 0x0503); +@@ -1089,6 +1092,16 @@ static int rtl822xb_config_init(struct p + return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020); + } + ++static int rtl822x_config_init(struct phy_device *phydev) ++{ ++ return rtl822x_set_serdes_option_mode(phydev, true); ++} ++ ++static int rtl822xb_config_init(struct phy_device *phydev) ++{ ++ return rtl822x_set_serdes_option_mode(phydev, false); ++} ++ + static int rtl822xb_get_rate_matching(struct phy_device *phydev, + phy_interface_t iface) + { +@@ -1678,7 +1691,8 @@ static struct phy_driver realtek_drvs[] + .soft_reset = rtl822x_c45_soft_reset, + .get_features = rtl822x_c45_get_features, + .config_aneg = rtl822x_c45_config_aneg, +- .read_status = rtl822x_c45_read_status, ++ .config_init = rtl822x_config_init, ++ .read_status = rtl822xb_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, + }, { diff --git a/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch b/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch new file mode 100644 index 00000000000..c22231ea662 --- /dev/null +++ b/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch @@ -0,0 +1,303 @@ +From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:45 +0200 +Subject: [PATCH] net: phy: pass PHY driver to .match_phy_device OP + +Pass PHY driver pointer to .match_phy_device OP in addition to phydev. +Having access to the PHY driver struct might be useful to check the +PHY ID of the driver is being matched for in case the PHY ID scanned in +the phydev is not consistent. + +A scenario for this is a PHY that change PHY ID after a firmware is +loaded, in such case, the PHY ID stored in PHY device struct is not +valid anymore and PHY will manually scan the ID in the match_phy_device +function. + +Having the PHY driver info is also useful for those PHY driver that +implement multiple simple .match_phy_device OP to match specific MMD PHY +ID. With this extra info if the parsing logic is the same, the matching +function can be generalized by using the phy_id in the PHY driver +instead of hardcoding. + +Rust wrapper callback is updated to align to the new match_phy_device +arguments. + +Suggested-by: Russell King (Oracle) +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Reviewed-by: Benno Lossin # for Rust +Reviewed-by: FUJITA Tomonori +Link: https://patch.msgid.link/20250517201353.5137-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/bcm87xx.c | 6 ++++-- + drivers/net/phy/icplus.c | 6 ++++-- + drivers/net/phy/marvell10g.c | 12 ++++++++---- + drivers/net/phy/micrel.c | 6 ++++-- + drivers/net/phy/nxp-c45-tja11xx.c | 12 ++++++++---- + drivers/net/phy/nxp-tja11xx.c | 6 ++++-- + drivers/net/phy/phy_device.c | 2 +- + drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++--------- + drivers/net/phy/teranetics.c | 3 ++- + include/linux/phy.h | 3 ++- + rust/kernel/net/phy.rs | 1 + + 11 files changed, 56 insertions(+), 28 deletions(-) + +--- a/drivers/net/phy/bcm87xx.c ++++ b/drivers/net/phy/bcm87xx.c +@@ -185,12 +185,14 @@ static irqreturn_t bcm87xx_handle_interr + return IRQ_HANDLED; + } + +-static int bcm8706_match_phy_device(struct phy_device *phydev) ++static int bcm8706_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706; + } + +-static int bcm8727_match_phy_device(struct phy_device *phydev) ++static int bcm8727_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727; + } +--- a/drivers/net/phy/icplus.c ++++ b/drivers/net/phy/icplus.c +@@ -520,12 +520,14 @@ static int ip101a_g_match_phy_device(str + return ip101a == !ret; + } + +-static int ip101a_match_phy_device(struct phy_device *phydev) ++static int ip101a_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ip101a_g_match_phy_device(phydev, true); + } + +-static int ip101g_match_phy_device(struct phy_device *phydev) ++static int ip101g_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ip101a_g_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -1284,7 +1284,8 @@ static int mv3310_get_number_of_ports(st + return ret + 1; + } + +-static int mv3310_match_phy_device(struct phy_device *phydev) ++static int mv3310_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +@@ -1293,7 +1294,8 @@ static int mv3310_match_phy_device(struc + return mv3310_get_number_of_ports(phydev) == 1; + } + +-static int mv3340_match_phy_device(struct phy_device *phydev) ++static int mv3340_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +@@ -1317,12 +1319,14 @@ static int mv211x_match_phy_device(struc + return !!(val & MDIO_PCS_SPEED_5G) == has_5g; + } + +-static int mv2110_match_phy_device(struct phy_device *phydev) ++static int mv2110_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return mv211x_match_phy_device(phydev, true); + } + +-static int mv2111_match_phy_device(struct phy_device *phydev) ++static int mv2111_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return mv211x_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -768,7 +768,8 @@ static int ksz8051_ksz8795_match_phy_dev + return !ret; + } + +-static int ksz8051_match_phy_device(struct phy_device *phydev) ++static int ksz8051_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ksz8051_ksz8795_match_phy_device(phydev, true); + } +@@ -888,7 +889,8 @@ static int ksz8061_config_init(struct ph + return kszphy_config_init(phydev); + } + +-static int ksz8795_match_phy_device(struct phy_device *phydev) ++static int ksz8795_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ksz8051_ksz8795_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/nxp-c45-tja11xx.c ++++ b/drivers/net/phy/nxp-c45-tja11xx.c +@@ -1944,13 +1944,15 @@ static int nxp_c45_macsec_ability(struct + return macsec_ability; + } + +-static int tja1103_match_phy_device(struct phy_device *phydev) ++static int tja1103_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) && + !nxp_c45_macsec_ability(phydev); + } + +-static int tja1104_match_phy_device(struct phy_device *phydev) ++static int tja1104_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) && + nxp_c45_macsec_ability(phydev); +--- a/drivers/net/phy/nxp-tja11xx.c ++++ b/drivers/net/phy/nxp-tja11xx.c +@@ -646,12 +646,14 @@ static int tja1102_match_phy_device(stru + return !ret; + } + +-static int tja1102_p0_match_phy_device(struct phy_device *phydev) ++static int tja1102_p0_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return tja1102_match_phy_device(phydev, true); + } + +-static int tja1102_p1_match_phy_device(struct phy_device *phydev) ++static int tja1102_p1_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return tja1102_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -600,7 +600,7 @@ static int phy_bus_match(struct device * + return 0; + + if (phydrv->match_phy_device) +- return phydrv->match_phy_device(phydev); ++ return phydrv->match_phy_device(phydev, phydrv); + + if (phydev->is_c45) { + for (i = 1; i < num_ids; i++) { +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1343,13 +1343,15 @@ static bool rtlgen_supports_mmd(struct p + return val > 0; + } + +-static int rtlgen_match_phy_device(struct phy_device *phydev) ++static int rtlgen_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_GENERIC_PHYID && + !rtlgen_supports_2_5gbps(phydev); + } + +-static int rtl8226_match_phy_device(struct phy_device *phydev) ++static int rtl8226_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_GENERIC_PHYID && + rtlgen_supports_2_5gbps(phydev) && +@@ -1365,32 +1367,38 @@ static int rtlgen_is_c45_match(struct ph + return !is_c45 && (id == phydev->phy_id); + } + +-static int rtl8221b_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev); + } + +-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); + } + +-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true); + } + +-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false); + } + +-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true); + } + +-static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev) ++static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if (phydev->is_c45) + return false; +@@ -1409,7 +1417,8 @@ static int rtl_internal_nbaset_match_phy + return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev); + } + +-static int rtl8251b_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8251B, true); + } +--- a/drivers/net/phy/teranetics.c ++++ b/drivers/net/phy/teranetics.c +@@ -67,7 +67,8 @@ static int teranetics_read_status(struct + return 0; + } + +-static int teranetics_match_phy_device(struct phy_device *phydev) ++static int teranetics_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020; + } +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1045,7 +1045,8 @@ struct phy_driver { + * driver for the given phydev. If NULL, matching is based on + * phy_id and phy_id_mask. + */ +- int (*match_phy_device)(struct phy_device *phydev); ++ int (*match_phy_device)(struct phy_device *phydev, ++ const struct phy_driver *phydrv); + + /** + * @set_wol: Some devices (e.g. qnap TS-119P II) require PHY +--- a/rust/kernel/net/phy.rs ++++ b/rust/kernel/net/phy.rs +@@ -421,6 +421,7 @@ impl Adapter { + /// `phydev` must be passed by the corresponding callback in `phy_driver`. + unsafe extern "C" fn match_phy_device_callback( + phydev: *mut bindings::phy_device, ++ _phydrv: *const bindings::phy_driver, + ) -> crate::ffi::c_int { + // SAFETY: This callback is called only in contexts + // where we hold `phy_device->lock`, so the accessors on diff --git a/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch b/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch new file mode 100644 index 00000000000..fed9d3c11fa --- /dev/null +++ b/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch @@ -0,0 +1,109 @@ +From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:48 +0200 +Subject: [PATCH 4/5] net: phy: introduce genphy_match_phy_device() + +Introduce new API, genphy_match_phy_device(), to provide a way to check +to match a PHY driver for a PHY device based on the info stored in the +PHY device struct. + +The function generalize the logic used in phy_bus_match() to check the +PHY ID whether if C45 or C22 ID should be used for matching. + +This is useful for custom .match_phy_device function that wants to use +the generic logic under some condition. (example a PHY is already setup +and provide the correct PHY ID) + +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-5-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy_device.c | 52 +++++++++++++++++++++++++----------- + include/linux/phy.h | 3 +++ + 2 files changed, 40 insertions(+), 15 deletions(-) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -589,20 +589,26 @@ static int phy_scan_fixups(struct phy_de + return 0; + } + +-static int phy_bus_match(struct device *dev, const struct device_driver *drv) ++/** ++ * genphy_match_phy_device - match a PHY device with a PHY driver ++ * @phydev: target phy_device struct ++ * @phydrv: target phy_driver struct ++ * ++ * Description: Checks whether the given PHY device matches the specified ++ * PHY driver. For Clause 45 PHYs, iterates over the available device ++ * identifiers and compares them against the driver's expected PHY ID, ++ * applying the provided mask. For Clause 22 PHYs, a direct ID comparison ++ * is performed. ++ * ++ * Return: 1 if the PHY device matches the driver, 0 otherwise. ++ */ ++int genphy_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { +- struct phy_device *phydev = to_phy_device(dev); +- const struct phy_driver *phydrv = to_phy_driver(drv); +- const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); +- int i; +- +- if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) +- return 0; +- +- if (phydrv->match_phy_device) +- return phydrv->match_phy_device(phydev, phydrv); +- + if (phydev->is_c45) { ++ const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); ++ int i; ++ + for (i = 1; i < num_ids; i++) { + if (phydev->c45_ids.device_ids[i] == 0xffffffff) + continue; +@@ -611,11 +617,27 @@ static int phy_bus_match(struct device * + phydrv->phy_id, phydrv->phy_id_mask)) + return 1; + } ++ + return 0; +- } else { +- return phy_id_compare(phydev->phy_id, phydrv->phy_id, +- phydrv->phy_id_mask); + } ++ ++ return phy_id_compare(phydev->phy_id, phydrv->phy_id, ++ phydrv->phy_id_mask); ++} ++EXPORT_SYMBOL_GPL(genphy_match_phy_device); ++ ++static int phy_bus_match(struct device *dev, const struct device_driver *drv) ++{ ++ struct phy_device *phydev = to_phy_device(dev); ++ const struct phy_driver *phydrv = to_phy_driver(drv); ++ ++ if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) ++ return 0; ++ ++ if (phydrv->match_phy_device) ++ return phydrv->match_phy_device(phydev, phydrv); ++ ++ return genphy_match_phy_device(phydev, phydrv); + } + + static ssize_t +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1963,6 +1963,9 @@ char *phy_attached_info_irq(struct phy_d + __malloc; + void phy_attached_info(struct phy_device *phydev); + ++int genphy_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv); ++ + /* Clause 22 PHY */ + int genphy_read_abilities(struct phy_device *phydev); + int genphy_setup_forced(struct phy_device *phydev); diff --git a/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch b/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch new file mode 100644 index 00000000000..f1bcb35e666 --- /dev/null +++ b/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch @@ -0,0 +1,1180 @@ +From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:49 +0200 +Subject: [PATCH 5/5] net: phy: Add support for Aeonsemi AS21xxx PHYs + +Add support for Aeonsemi AS21xxx 10G C45 PHYs. These PHYs integrate +an IPC to setup some configuration and require special handling to +sync with the parity bit. The parity bit is a way the IPC use to +follow correct order of command sent. + +Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1, +AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1, +AS21210PB1 that all register with the PHY ID 0x7500 0x7510 +before the firmware is loaded. + +They all support up to 5 LEDs with various HW mode supported. + +While implementing it was found some strange coincidence with using the +same logic for implementing C22 in MMD regs in Broadcom PHYs. + +For reference here the AS21xxx PHY name logic: + +AS21x1xxB1 + ^ ^^ + | |J: Supports SyncE/PTP + | |P: No SyncE/PTP support + | 1: Supports 2nd Serdes + | 2: Not 2nd Serdes support + 0: 10G, 5G, 2.5G + 5: 5G, 2.5G + 2: 2.5G + +Reviewed-by: Andrew Lunn +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + MAINTAINERS | 6 + + drivers/net/phy/Kconfig | 12 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/as21xxx.c | 1087 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 1106 insertions(+) + create mode 100644 drivers/net/phy/as21xxx.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -637,6 +637,12 @@ F: drivers/iio/accel/adxl380.h + F: drivers/iio/accel/adxl380_i2c.c + F: drivers/iio/accel/adxl380_spi.c + ++AEONSEMI PHY DRIVER ++M: Christian Marangi ++L: netdev@vger.kernel.org ++S: Maintained ++F: drivers/net/phy/as21xxx.c ++ + AF8133J THREE-AXIS MAGNETOMETER DRIVER + M: Ondřej Jirman + S: Maintained +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -79,6 +79,18 @@ config SFP + + comment "MII PHY device drivers" + ++config AS21XXX_PHY ++ tristate "Aeonsemi AS21xxx PHYs" ++ help ++ Currently supports the Aeonsemi AS21xxx PHY. ++ ++ These are C45 PHYs 10G that require all a generic firmware. ++ ++ Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1, ++ AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1, ++ AS21210PB1 that all register with the PHY ID 0x7500 0x7500 ++ before the firmware is loaded. ++ + config AIR_EN8811H_PHY + tristate "Airoha EN8811H 2.5 Gigabit PHY" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_AIR_EN8811H_PHY) += air_e + obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_AMCC_QT2025_PHY) += qt2025.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ ++obj-$(CONFIG_AS21XXX_PHY) += as21xxx.o + ifdef CONFIG_AX88796B_RUST_PHY + obj-$(CONFIG_AX88796B_PHY) += ax88796b_rust.o + else +--- /dev/null ++++ b/drivers/net/phy/as21xxx.c +@@ -0,0 +1,1087 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Aeonsemi AS21XXxX PHY Driver ++ * ++ * Author: Christian Marangi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR 0x3 ++#define VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR 0x4 ++ ++#define VEND1_GLB_REG_CPU_CTRL 0xe ++#define VEND1_GLB_CPU_CTRL_MASK GENMASK(4, 0) ++#define VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK GENMASK(12, 8) ++#define VEND1_GLB_CPU_CTRL_LED_POLARITY(_n) FIELD_PREP(VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK, \ ++ BIT(_n)) ++ ++#define VEND1_FW_START_ADDR 0x100 ++ ++#define VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD 0x101 ++#define VEND1_GLB_REG_MDIO_INDIRECT_LOAD 0x102 ++ ++#define VEND1_GLB_REG_MDIO_INDIRECT_STATUS 0x103 ++ ++#define VEND1_PTP_CLK 0x142 ++#define VEND1_PTP_CLK_EN BIT(6) ++ ++/* 5 LED at step of 0x20 ++ * FE: Fast-Ethernet (10/100) ++ * GE: Gigabit-Ethernet (1000) ++ * NG: New-Generation (2500/5000/10000) ++ */ ++#define VEND1_LED_REG(_n) (0x1800 + ((_n) * 0x10)) ++#define VEND1_LED_REG_A_EVENT GENMASK(15, 11) ++#define VEND1_LED_CONF 0x1881 ++#define VEND1_LED_CONFG_BLINK GENMASK(7, 0) ++ ++#define VEND1_SPEED_STATUS 0x4002 ++#define VEND1_SPEED_MASK GENMASK(7, 0) ++#define VEND1_SPEED_10000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x3) ++#define VEND1_SPEED_5000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x5) ++#define VEND1_SPEED_2500 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x9) ++#define VEND1_SPEED_1000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x10) ++#define VEND1_SPEED_100 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x20) ++#define VEND1_SPEED_10 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x0) ++ ++#define VEND1_IPC_CMD 0x5801 ++#define AEON_IPC_CMD_PARITY BIT(15) ++#define AEON_IPC_CMD_SIZE GENMASK(10, 6) ++#define AEON_IPC_CMD_OPCODE GENMASK(5, 0) ++ ++#define IPC_CMD_NOOP 0x0 /* Do nothing */ ++#define IPC_CMD_INFO 0x1 /* Get Firmware Version */ ++#define IPC_CMD_SYS_CPU 0x2 /* SYS_CPU */ ++#define IPC_CMD_BULK_DATA 0xa /* Pass bulk data in ipc registers. */ ++#define IPC_CMD_BULK_WRITE 0xc /* Write bulk data to memory */ ++#define IPC_CMD_CFG_PARAM 0x1a /* Write config parameters to memory */ ++#define IPC_CMD_NG_TESTMODE 0x1b /* Set NG test mode and tone */ ++#define IPC_CMD_TEMP_MON 0x15 /* Temperature monitoring function */ ++#define IPC_CMD_SET_LED 0x23 /* Set led */ ++ ++#define VEND1_IPC_STS 0x5802 ++#define AEON_IPC_STS_PARITY BIT(15) ++#define AEON_IPC_STS_SIZE GENMASK(14, 10) ++#define AEON_IPC_STS_OPCODE GENMASK(9, 4) ++#define AEON_IPC_STS_STATUS GENMASK(3, 0) ++#define AEON_IPC_STS_STATUS_RCVD FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x1) ++#define AEON_IPC_STS_STATUS_PROCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x2) ++#define AEON_IPC_STS_STATUS_SUCCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x4) ++#define AEON_IPC_STS_STATUS_ERROR FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x8) ++#define AEON_IPC_STS_STATUS_BUSY FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xe) ++#define AEON_IPC_STS_STATUS_READY FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xf) ++ ++#define VEND1_IPC_DATA0 0x5808 ++#define VEND1_IPC_DATA1 0x5809 ++#define VEND1_IPC_DATA2 0x580a ++#define VEND1_IPC_DATA3 0x580b ++#define VEND1_IPC_DATA4 0x580c ++#define VEND1_IPC_DATA5 0x580d ++#define VEND1_IPC_DATA6 0x580e ++#define VEND1_IPC_DATA7 0x580f ++#define VEND1_IPC_DATA(_n) (VEND1_IPC_DATA0 + (_n)) ++ ++/* Sub command of CMD_INFO */ ++#define IPC_INFO_VERSION 0x1 ++ ++/* Sub command of CMD_SYS_CPU */ ++#define IPC_SYS_CPU_REBOOT 0x3 ++#define IPC_SYS_CPU_IMAGE_OFST 0x4 ++#define IPC_SYS_CPU_IMAGE_CHECK 0x5 ++#define IPC_SYS_CPU_PHY_ENABLE 0x6 ++ ++/* Sub command of CMD_CFG_PARAM */ ++#define IPC_CFG_PARAM_DIRECT 0x4 ++ ++/* CFG DIRECT sub command */ ++#define IPC_CFG_PARAM_DIRECT_NG_PHYCTRL 0x1 ++#define IPC_CFG_PARAM_DIRECT_CU_AN 0x2 ++#define IPC_CFG_PARAM_DIRECT_SDS_PCS 0x3 ++#define IPC_CFG_PARAM_DIRECT_AUTO_EEE 0x4 ++#define IPC_CFG_PARAM_DIRECT_SDS_PMA 0x5 ++#define IPC_CFG_PARAM_DIRECT_DPC_RA 0x6 ++#define IPC_CFG_PARAM_DIRECT_DPC_PKT_CHK 0x7 ++#define IPC_CFG_PARAM_DIRECT_DPC_SDS_WAIT_ETH 0x8 ++#define IPC_CFG_PARAM_DIRECT_WDT 0x9 ++#define IPC_CFG_PARAM_DIRECT_SDS_RESTART_AN 0x10 ++#define IPC_CFG_PARAM_DIRECT_TEMP_MON 0x11 ++#define IPC_CFG_PARAM_DIRECT_WOL 0x12 ++ ++/* Sub command of CMD_TEMP_MON */ ++#define IPC_CMD_TEMP_MON_GET 0x4 ++ ++#define AS21XXX_MDIO_AN_C22 0xffe0 ++ ++#define PHY_ID_AS21XXX 0x75009410 ++/* AS21xxx ID Legend ++ * AS21x1xxB1 ++ * ^ ^^ ++ * | |J: Supports SyncE/PTP ++ * | |P: No SyncE/PTP support ++ * | 1: Supports 2nd Serdes ++ * | 2: Not 2nd Serdes support ++ * 0: 10G, 5G, 2.5G ++ * 5: 5G, 2.5G ++ * 2: 2.5G ++ */ ++#define PHY_ID_AS21011JB1 0x75009402 ++#define PHY_ID_AS21011PB1 0x75009412 ++#define PHY_ID_AS21010JB1 0x75009422 ++#define PHY_ID_AS21010PB1 0x75009432 ++#define PHY_ID_AS21511JB1 0x75009442 ++#define PHY_ID_AS21511PB1 0x75009452 ++#define PHY_ID_AS21510JB1 0x75009462 ++#define PHY_ID_AS21510PB1 0x75009472 ++#define PHY_ID_AS21210JB1 0x75009482 ++#define PHY_ID_AS21210PB1 0x75009492 ++#define PHY_VENDOR_AEONSEMI 0x75009400 ++ ++#define AEON_MAX_LEDS 5 ++#define AEON_IPC_DELAY 10000 ++#define AEON_IPC_TIMEOUT (AEON_IPC_DELAY * 100) ++#define AEON_IPC_DATA_NUM_REGISTERS 8 ++#define AEON_IPC_DATA_MAX (AEON_IPC_DATA_NUM_REGISTERS * sizeof(u16)) ++ ++#define AEON_BOOT_ADDR 0x1000 ++#define AEON_CPU_BOOT_ADDR 0x2000 ++#define AEON_CPU_CTRL_FW_LOAD (BIT(4) | BIT(2) | BIT(1) | BIT(0)) ++#define AEON_CPU_CTRL_FW_START BIT(0) ++ ++enum as21xxx_led_event { ++ VEND1_LED_REG_A_EVENT_ON_10 = 0x0, ++ VEND1_LED_REG_A_EVENT_ON_100, ++ VEND1_LED_REG_A_EVENT_ON_1000, ++ VEND1_LED_REG_A_EVENT_ON_2500, ++ VEND1_LED_REG_A_EVENT_ON_5000, ++ VEND1_LED_REG_A_EVENT_ON_10000, ++ VEND1_LED_REG_A_EVENT_ON_FE_GE, ++ VEND1_LED_REG_A_EVENT_ON_NG, ++ VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX, ++ VEND1_LED_REG_A_EVENT_ON_COLLISION, ++ VEND1_LED_REG_A_EVENT_BLINK_TX, ++ VEND1_LED_REG_A_EVENT_BLINK_RX, ++ VEND1_LED_REG_A_EVENT_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_LINK, ++ VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX, ++ VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_NG_BLINK_FE_GE, ++ VEND1_LED_REG_A_EVENT_ON_FD_BLINK_COLLISION, ++ VEND1_LED_REG_A_EVENT_ON, ++ VEND1_LED_REG_A_EVENT_OFF, ++}; ++ ++struct as21xxx_led_pattern_info { ++ unsigned int pattern; ++ u16 val; ++}; ++ ++struct as21xxx_priv { ++ bool parity_status; ++ /* Protect concurrent IPC access */ ++ struct mutex ipc_lock; ++}; ++ ++static struct as21xxx_led_pattern_info as21xxx_led_supported_pattern[] = { ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10), ++ .val = VEND1_LED_REG_A_EVENT_ON_10 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_100), ++ .val = VEND1_LED_REG_A_EVENT_ON_100 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_1000), ++ .val = VEND1_LED_REG_A_EVENT_ON_1000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500), ++ .val = VEND1_LED_REG_A_EVENT_ON_2500 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_5000), ++ .val = VEND1_LED_REG_A_EVENT_ON_5000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_10000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000), ++ .val = VEND1_LED_REG_A_EVENT_ON_FE_GE ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_NG ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_FULL_DUPLEX), ++ .val = VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_TX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_TX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_RX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT ++ } ++}; ++ ++static int aeon_firmware_boot(struct phy_device *phydev, const u8 *data, ++ size_t size) ++{ ++ int i, ret; ++ u16 val; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL, ++ VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_LOAD); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_FW_START_ADDR, ++ AEON_BOOT_ADDR); ++ if (ret) ++ return ret; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD, ++ 0x3ffc, 0xc000); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_STATUS); ++ if (val > 1) { ++ phydev_err(phydev, "wrong origin mdio_indirect_status: %x\n", val); ++ return -EINVAL; ++ } ++ ++ /* Firmware is always aligned to u16 */ ++ for (i = 0; i < size; i += 2) { ++ val = data[i + 1] << 8 | data[i]; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_LOAD, val); ++ if (ret) ++ return ret; ++ } ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR, ++ lower_16_bits(AEON_CPU_BOOT_ADDR)); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR, ++ upper_16_bits(AEON_CPU_BOOT_ADDR)); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL, ++ VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_START); ++} ++ ++static int aeon_firmware_load(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ const struct firmware *fw; ++ const char *fw_name; ++ int ret; ++ ++ ret = of_property_read_string(dev->of_node, "firmware-name", ++ &fw_name); ++ if (ret) ++ return ret; ++ ++ ret = request_firmware(&fw, fw_name, dev); ++ if (ret) { ++ phydev_err(phydev, "failed to find FW file %s (%d)\n", ++ fw_name, ret); ++ return ret; ++ } ++ ++ ret = aeon_firmware_boot(phydev, fw->data, fw->size); ++ ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++static bool aeon_ipc_ready(u16 val, bool parity_status) ++{ ++ u16 status; ++ ++ if (FIELD_GET(AEON_IPC_STS_PARITY, val) != parity_status) ++ return false; ++ ++ status = val & AEON_IPC_STS_STATUS; ++ ++ return status != AEON_IPC_STS_STATUS_RCVD && ++ status != AEON_IPC_STS_STATUS_PROCESS && ++ status != AEON_IPC_STS_STATUS_BUSY; ++} ++ ++static int aeon_ipc_wait_cmd(struct phy_device *phydev, bool parity_status) ++{ ++ u16 val; ++ ++ /* Exit condition logic: ++ * - Wait for parity bit equal ++ * - Wait for status success, error OR ready ++ */ ++ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS, val, ++ aeon_ipc_ready(val, parity_status), ++ AEON_IPC_DELAY, AEON_IPC_TIMEOUT, false); ++} ++ ++static int aeon_ipc_send_cmd(struct phy_device *phydev, ++ struct as21xxx_priv *priv, ++ u16 cmd, u16 *ret_sts) ++{ ++ bool curr_parity; ++ int ret; ++ ++ /* The IPC sync by using a single parity bit. ++ * Each CMD have alternately this bit set or clear ++ * to understand correct flow and packet order. ++ */ ++ curr_parity = priv->parity_status; ++ if (priv->parity_status) ++ cmd |= AEON_IPC_CMD_PARITY; ++ ++ /* Always update parity for next packet */ ++ priv->parity_status = !priv->parity_status; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd); ++ if (ret) ++ return ret; ++ ++ /* Wait for packet to be processed */ ++ usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000); ++ ++ /* With no ret_sts, ignore waiting for packet completion ++ * (ipc parity bit sync) ++ */ ++ if (!ret_sts) ++ return 0; ++ ++ ret = aeon_ipc_wait_cmd(phydev, curr_parity); ++ if (ret) ++ return ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS); ++ if (ret < 0) ++ return ret; ++ ++ *ret_sts = ret; ++ if ((*ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_SUCCESS) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* If data is NULL, return 0 or negative error. ++ * If data not NULL, return number of Bytes received from IPC or ++ * a negative error. ++ */ ++static int aeon_ipc_send_msg(struct phy_device *phydev, ++ u16 opcode, u16 *data, unsigned int data_len, ++ u16 *ret_data) ++{ ++ struct as21xxx_priv *priv = phydev->priv; ++ unsigned int ret_size; ++ u16 cmd, ret_sts; ++ int ret; ++ int i; ++ ++ /* IPC have a max of 8 register to transfer data, ++ * make sure we never exceed this. ++ */ ++ if (data_len > AEON_IPC_DATA_MAX) ++ return -EINVAL; ++ ++ for (i = 0; i < data_len / sizeof(u16); i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i), ++ data[i]); ++ ++ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) | ++ FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode); ++ ++ mutex_lock(&priv->ipc_lock); ++ ++ ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts); ++ if (ret) { ++ phydev_err(phydev, "failed to send ipc msg for %x: %d\n", ++ opcode, ret); ++ goto out; ++ } ++ ++ if (!data) ++ goto out; ++ ++ if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Prevent IPC from stack smashing the kernel. ++ * We can't trust IPC to return a good value and we always ++ * preallocate space for 16 Bytes. ++ */ ++ ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts); ++ if (ret_size > AEON_IPC_DATA_MAX) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Read data from IPC data register for ret_size value from IPC */ ++ for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) { ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i)); ++ if (ret < 0) ++ goto out; ++ ++ ret_data[i] = ret; ++ } ++ ++ ret = ret_size; ++ ++out: ++ mutex_unlock(&priv->ipc_lock); ++ ++ return ret; ++} ++ ++static int aeon_ipc_noop(struct phy_device *phydev, ++ struct as21xxx_priv *priv, u16 *ret_sts) ++{ ++ u16 cmd; ++ ++ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) | ++ FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_NOOP); ++ ++ return aeon_ipc_send_cmd(phydev, priv, cmd, ret_sts); ++} ++ ++/* Logic to sync parity bit with IPC. ++ * We send 2 NOP cmd with same partity and we wait for IPC ++ * to handle the packet only for the second one. This way ++ * we make sure we are sync for every next cmd. ++ */ ++static int aeon_ipc_sync_parity(struct phy_device *phydev, ++ struct as21xxx_priv *priv) ++{ ++ u16 ret_sts; ++ int ret; ++ ++ mutex_lock(&priv->ipc_lock); ++ ++ /* Send NOP with no parity */ ++ aeon_ipc_noop(phydev, priv, NULL); ++ ++ /* Reset packet parity */ ++ priv->parity_status = false; ++ ++ /* Send second NOP with no parity */ ++ ret = aeon_ipc_noop(phydev, priv, &ret_sts); ++ ++ mutex_unlock(&priv->ipc_lock); ++ ++ /* We expect to return -EINVAL */ ++ if (ret != -EINVAL) ++ return ret; ++ ++ if ((ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_READY) { ++ phydev_err(phydev, "Invalid IPC status on sync parity: %x\n", ++ ret_sts); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aeon_ipc_get_fw_version(struct phy_device *phydev) ++{ ++ u16 ret_data[AEON_IPC_DATA_NUM_REGISTERS], data[1]; ++ char fw_version[AEON_IPC_DATA_MAX + 1]; ++ int ret; ++ ++ data[0] = IPC_INFO_VERSION; ++ ++ ret = aeon_ipc_send_msg(phydev, IPC_CMD_INFO, data, ++ sizeof(data), ret_data); ++ if (ret < 0) ++ return ret; ++ ++ /* Make sure FW version is NULL terminated */ ++ memcpy(fw_version, ret_data, ret); ++ fw_version[ret] = '\0'; ++ ++ phydev_info(phydev, "Firmware Version: %s\n", fw_version); ++ ++ return 0; ++} ++ ++static int aeon_dpc_ra_enable(struct phy_device *phydev) ++{ ++ u16 data[2]; ++ ++ data[0] = IPC_CFG_PARAM_DIRECT; ++ data[1] = IPC_CFG_PARAM_DIRECT_DPC_RA; ++ ++ return aeon_ipc_send_msg(phydev, IPC_CMD_CFG_PARAM, data, ++ sizeof(data), NULL); ++} ++ ++static int as21xxx_probe(struct phy_device *phydev) ++{ ++ struct as21xxx_priv *priv; ++ int ret; ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, ++ sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ phydev->priv = priv; ++ ++ ret = devm_mutex_init(&phydev->mdio.dev, ++ &priv->ipc_lock); ++ if (ret) ++ return ret; ++ ++ ret = aeon_ipc_sync_parity(phydev, priv); ++ if (ret) ++ return ret; ++ ++ ret = aeon_ipc_get_fw_version(phydev); ++ if (ret) ++ return ret; ++ ++ /* Enable PTP clk if not already Enabled */ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK, ++ VEND1_PTP_CLK_EN); ++ if (ret) ++ return ret; ++ ++ return aeon_dpc_ra_enable(phydev); ++} ++ ++static int as21xxx_read_link(struct phy_device *phydev, int *bmcr) ++{ ++ int status; ++ ++ /* Normal C22 BMCR report inconsistent data, use ++ * the mapped C22 in C45 to have more consistent link info. ++ */ ++ *bmcr = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_BMCR); ++ if (*bmcr < 0) ++ return *bmcr; ++ ++ /* Autoneg is being started, therefore disregard current ++ * link status and report link as down. ++ */ ++ if (*bmcr & BMCR_ANRESTART) { ++ phydev->link = 0; ++ return 0; ++ } ++ ++ status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); ++ if (status < 0) ++ return status; ++ ++ phydev->link = !!(status & MDIO_STAT1_LSTATUS); ++ ++ return 0; ++} ++ ++static int as21xxx_read_c22_lpa(struct phy_device *phydev) ++{ ++ int lpagb; ++ ++ /* MII_STAT1000 are only filled in the mapped C22 ++ * in C45, use that to fill lpagb values and check. ++ */ ++ lpagb = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_STAT1000); ++ if (lpagb < 0) ++ return lpagb; ++ ++ if (lpagb & LPA_1000MSFAIL) { ++ int adv = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_CTRL1000); ++ ++ if (adv < 0) ++ return adv; ++ ++ if (adv & CTL1000_ENABLE_MASTER) ++ phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n"); ++ else ++ phydev_err(phydev, "Master/Slave resolution failed\n"); ++ return -ENOLINK; ++ } ++ ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ++ lpagb); ++ ++ return 0; ++} ++ ++static int as21xxx_read_status(struct phy_device *phydev) ++{ ++ int bmcr, old_link = phydev->link; ++ int ret; ++ ++ ret = as21xxx_read_link(phydev, &bmcr); ++ if (ret) ++ return ret; ++ ++ /* why bother the PHY if nothing can have changed */ ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ return 0; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE) { ++ ret = genphy_c45_read_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ ret = as21xxx_read_c22_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ phy_resolve_aneg_linkmode(phydev); ++ } else { ++ int speed; ++ ++ linkmode_zero(phydev->lp_advertising); ++ ++ speed = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_SPEED_STATUS); ++ if (speed < 0) ++ return speed; ++ ++ switch (speed & VEND1_SPEED_STATUS) { ++ case VEND1_SPEED_10000: ++ phydev->speed = SPEED_10000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_5000: ++ phydev->speed = SPEED_5000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ if (bmcr & BMCR_FULLDPLX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ break; ++ case VEND1_SPEED_100: ++ phydev->speed = SPEED_100; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_10: ++ phydev->speed = SPEED_10; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int as21xxx_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 val = VEND1_LED_REG_A_EVENT_OFF; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ if (value) ++ val = VEND1_LED_REG_A_EVENT_ON; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_LED_REG(index), ++ VEND1_LED_REG_A_EVENT, ++ FIELD_PREP(VEND1_LED_REG_A_EVENT, val)); ++} ++ ++static int as21xxx_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ int i; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (rules == as21xxx_led_supported_pattern[i].pattern) ++ return 0; ++ ++ return -EOPNOTSUPP; ++} ++ ++static int as21xxx_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ int i, val; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_LED_REG(index)); ++ if (val < 0) ++ return val; ++ ++ val = FIELD_GET(VEND1_LED_REG_A_EVENT, val); ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (val == as21xxx_led_supported_pattern[i].val) { ++ *rules = as21xxx_led_supported_pattern[i].pattern; ++ return 0; ++ } ++ ++ /* Should be impossible */ ++ return -EINVAL; ++} ++ ++static int as21xxx_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 val = 0; ++ int i; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (rules == as21xxx_led_supported_pattern[i].pattern) { ++ val = as21xxx_led_supported_pattern[i].val; ++ break; ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_LED_REG(index), ++ VEND1_LED_REG_A_EVENT, ++ FIELD_PREP(VEND1_LED_REG_A_EVENT, val)); ++} ++ ++static int as21xxx_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ bool led_active_low = false; ++ u16 mask, val = 0; ++ u32 mode; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ led_active_low = true; ++ break; ++ case PHY_LED_ACTIVE_HIGH: /* default mode */ ++ led_active_low = false; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ mask = VEND1_GLB_CPU_CTRL_LED_POLARITY(index); ++ if (led_active_low) ++ val = VEND1_GLB_CPU_CTRL_LED_POLARITY(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_CTRL, ++ mask, val); ++} ++ ++static int as21xxx_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) ++{ ++ struct as21xxx_priv *priv; ++ u16 ret_sts; ++ u32 phy_id; ++ int ret; ++ ++ /* Skip PHY that are not AS21xxx or already have firmware loaded */ ++ if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX) ++ return genphy_match_phy_device(phydev, phydrv); ++ ++ /* Read PHY ID to handle firmware just loaded */ ++ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1); ++ if (ret < 0) ++ return ret; ++ phy_id = ret << 16; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID2); ++ if (ret < 0) ++ return ret; ++ phy_id |= ret; ++ ++ /* With PHY ID not the generic AS21xxx one assume ++ * the firmware just loaded ++ */ ++ if (phy_id != PHY_ID_AS21XXX) ++ return phy_id == phydrv->phy_id; ++ ++ /* Allocate temp priv and load the firmware */ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ mutex_init(&priv->ipc_lock); ++ ++ ret = aeon_firmware_load(phydev); ++ if (ret) ++ goto out; ++ ++ /* Sync parity... */ ++ ret = aeon_ipc_sync_parity(phydev, priv); ++ if (ret) ++ goto out; ++ ++ /* ...and send a third NOOP cmd to wait for firmware finish loading */ ++ ret = aeon_ipc_noop(phydev, priv, &ret_sts); ++ if (ret) ++ goto out; ++ ++out: ++ mutex_destroy(&priv->ipc_lock); ++ kfree(priv); ++ ++ /* Return can either be 0 or a negative error code. ++ * Returning 0 here means THIS is NOT a suitable PHY. ++ * ++ * For the specific case of the generic Aeonsemi PHY ID that ++ * needs the firmware the be loaded first to have a correct PHY ID, ++ * this is OK as a matching PHY ID will be found right after. ++ * This relies on the driver probe order where the first PHY driver ++ * probed is the generic one. ++ */ ++ return ret; ++} ++ ++static struct phy_driver as21xxx_drivers[] = { ++ { ++ /* PHY expose in C45 as 0x7500 0x9410 ++ * before firmware is loaded. ++ * This driver entry must be attempted first to load ++ * the firmware and thus update the ID registers. ++ */ ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21XXX), ++ .name = "Aeonsemi AS21xxx", ++ .match_phy_device = as21xxx_match_phy_device, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21011JB1), ++ .name = "Aeonsemi AS21011JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1), ++ .name = "Aeonsemi AS21011PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1), ++ .name = "Aeonsemi AS21010PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1), ++ .name = "Aeonsemi AS21010JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1), ++ .name = "Aeonsemi AS21210PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1), ++ .name = "Aeonsemi AS21510JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1), ++ .name = "Aeonsemi AS21510PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1), ++ .name = "Aeonsemi AS21511JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1), ++ .name = "Aeonsemi AS21210JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1), ++ .name = "Aeonsemi AS21511PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++}; ++module_phy_driver(as21xxx_drivers); ++ ++static struct mdio_device_id __maybe_unused as21xxx_tbl[] = { ++ { PHY_ID_MATCH_VENDOR(PHY_VENDOR_AEONSEMI) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mdio, as21xxx_tbl); ++ ++MODULE_DESCRIPTION("Aeonsemi AS21xxx PHY driver"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.12/783-01-v6.18-net-dsa-Move-KS8995-to-the-DSA-subsystem.patch b/target/linux/generic/backport-6.12/783-01-v6.18-net-dsa-Move-KS8995-to-the-DSA-subsystem.patch new file mode 100644 index 00000000000..b91c0cd8cfd --- /dev/null +++ b/target/linux/generic/backport-6.12/783-01-v6.18-net-dsa-Move-KS8995-to-the-DSA-subsystem.patch @@ -0,0 +1,1092 @@ +From 9c370f7f92b0232fe925ca2caefb5e3126a0357d Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 13 Aug 2025 23:43:03 +0200 +Subject: [PATCH 1/4] net: dsa: Move KS8995 to the DSA subsystem + +By reading the datasheets for the KS8995 it is obvious that this +is a 100 Mbit DSA switch. + +Let us start the refactoring by moving it to the DSA subsystem to +preserve development history. + +Verified that the chip still probes the same after this patch +provided CONFIG_HAVE_NET_DSA, CONFIG_NET_DSA and CONFIG_DSA_KS8995 +are selected. + +Signed-off-by: Linus Walleij +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250813-ks8995-to-dsa-v1-1-75c359ede3a5@linaro.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/Kconfig | 7 +++++++ + drivers/net/dsa/Makefile | 1 + + drivers/net/{phy/spi_ks8995.c => dsa/ks8995.c} | 0 + drivers/net/phy/Kconfig | 4 ---- + drivers/net/phy/Makefile | 1 - + 5 files changed, 8 insertions(+), 5 deletions(-) + rename drivers/net/{phy/spi_ks8995.c => dsa/ks8995.c} (100%) + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -100,6 +100,13 @@ config NET_DSA_RZN1_A5PSW + This driver supports the A5PSW switch, which is embedded in Renesas + RZ/N1 SoC. + ++config NET_DSA_KS8995 ++ tristate "Micrel KS8995 family 5-ports 10/100 Ethernet switches" ++ depends on SPI ++ help ++ This driver supports the Micrel KS8995 family of 10/100 Mbit ethernet ++ switches, managed over SPI. ++ + config NET_DSA_SMSC_LAN9303 + tristate + select NET_DSA_TAG_LAN9303 +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -5,6 +5,7 @@ obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o + ifdef CONFIG_NET_DSA_LOOP + obj-$(CONFIG_FIXED_PHY) += dsa_loop_bdinfo.o + endif ++obj-$(CONFIG_NET_DSA_KS8995) += ks8995.o + obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o + obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o + obj-$(CONFIG_NET_DSA_MT7530_MDIO) += mt7530-mdio.o +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -443,7 +443,3 @@ config XILINX_GMII2RGMII + Ethernet physical media devices and the Gigabit Ethernet controller. + + endif # PHYLIB +- +-config MICREL_KS8995MA +- tristate "Micrel KS8995MA 5-ports 10/100 managed Ethernet switch" +- depends on SPI +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -77,7 +77,6 @@ obj-$(CONFIG_MARVELL_88X2222_PHY) += mar + obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o + obj-y += mediatek/ + obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o +-obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o + obj-$(CONFIG_MICREL_PHY) += micrel.o + obj-$(CONFIG_MICROCHIP_PHY) += microchip.o + obj-$(CONFIG_MICROCHIP_T1_PHY) += microchip_t1.o +--- /dev/null ++++ b/drivers/net/dsa/ks8995.c +@@ -0,0 +1,506 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * SPI driver for Micrel/Kendin KS8995M and KSZ8864RMN ethernet switches ++ * ++ * Copyright (C) 2008 Gabor Juhos ++ * ++ * This file was based on: drivers/spi/at25.c ++ * Copyright (C) 2006 David Brownell ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define DRV_VERSION "0.1.1" ++#define DRV_DESC "Micrel KS8995 Ethernet switch SPI driver" ++ ++/* ------------------------------------------------------------------------ */ ++ ++#define KS8995_REG_ID0 0x00 /* Chip ID0 */ ++#define KS8995_REG_ID1 0x01 /* Chip ID1 */ ++ ++#define KS8995_REG_GC0 0x02 /* Global Control 0 */ ++#define KS8995_REG_GC1 0x03 /* Global Control 1 */ ++#define KS8995_REG_GC2 0x04 /* Global Control 2 */ ++#define KS8995_REG_GC3 0x05 /* Global Control 3 */ ++#define KS8995_REG_GC4 0x06 /* Global Control 4 */ ++#define KS8995_REG_GC5 0x07 /* Global Control 5 */ ++#define KS8995_REG_GC6 0x08 /* Global Control 6 */ ++#define KS8995_REG_GC7 0x09 /* Global Control 7 */ ++#define KS8995_REG_GC8 0x0a /* Global Control 8 */ ++#define KS8995_REG_GC9 0x0b /* Global Control 9 */ ++ ++#define KS8995_REG_PC(p, r) ((0x10 * p) + r) /* Port Control */ ++#define KS8995_REG_PS(p, r) ((0x10 * p) + r + 0xe) /* Port Status */ ++ ++#define KS8995_REG_TPC0 0x60 /* TOS Priority Control 0 */ ++#define KS8995_REG_TPC1 0x61 /* TOS Priority Control 1 */ ++#define KS8995_REG_TPC2 0x62 /* TOS Priority Control 2 */ ++#define KS8995_REG_TPC3 0x63 /* TOS Priority Control 3 */ ++#define KS8995_REG_TPC4 0x64 /* TOS Priority Control 4 */ ++#define KS8995_REG_TPC5 0x65 /* TOS Priority Control 5 */ ++#define KS8995_REG_TPC6 0x66 /* TOS Priority Control 6 */ ++#define KS8995_REG_TPC7 0x67 /* TOS Priority Control 7 */ ++ ++#define KS8995_REG_MAC0 0x68 /* MAC address 0 */ ++#define KS8995_REG_MAC1 0x69 /* MAC address 1 */ ++#define KS8995_REG_MAC2 0x6a /* MAC address 2 */ ++#define KS8995_REG_MAC3 0x6b /* MAC address 3 */ ++#define KS8995_REG_MAC4 0x6c /* MAC address 4 */ ++#define KS8995_REG_MAC5 0x6d /* MAC address 5 */ ++ ++#define KS8995_REG_IAC0 0x6e /* Indirect Access Control 0 */ ++#define KS8995_REG_IAC1 0x6f /* Indirect Access Control 0 */ ++#define KS8995_REG_IAD7 0x70 /* Indirect Access Data 7 */ ++#define KS8995_REG_IAD6 0x71 /* Indirect Access Data 6 */ ++#define KS8995_REG_IAD5 0x72 /* Indirect Access Data 5 */ ++#define KS8995_REG_IAD4 0x73 /* Indirect Access Data 4 */ ++#define KS8995_REG_IAD3 0x74 /* Indirect Access Data 3 */ ++#define KS8995_REG_IAD2 0x75 /* Indirect Access Data 2 */ ++#define KS8995_REG_IAD1 0x76 /* Indirect Access Data 1 */ ++#define KS8995_REG_IAD0 0x77 /* Indirect Access Data 0 */ ++ ++#define KSZ8864_REG_ID1 0xfe /* Chip ID in bit 7 */ ++ ++#define KS8995_REGS_SIZE 0x80 ++#define KSZ8864_REGS_SIZE 0x100 ++#define KSZ8795_REGS_SIZE 0x100 ++ ++#define ID1_CHIPID_M 0xf ++#define ID1_CHIPID_S 4 ++#define ID1_REVISION_M 0x7 ++#define ID1_REVISION_S 1 ++#define ID1_START_SW 1 /* start the switch */ ++ ++#define FAMILY_KS8995 0x95 ++#define FAMILY_KSZ8795 0x87 ++#define CHIPID_M 0 ++#define KS8995_CHIP_ID 0x00 ++#define KSZ8864_CHIP_ID 0x01 ++#define KSZ8795_CHIP_ID 0x09 ++ ++#define KS8995_CMD_WRITE 0x02U ++#define KS8995_CMD_READ 0x03U ++ ++#define KS8995_RESET_DELAY 10 /* usec */ ++ ++enum ks8995_chip_variant { ++ ks8995, ++ ksz8864, ++ ksz8795, ++ max_variant ++}; ++ ++struct ks8995_chip_params { ++ char *name; ++ int family_id; ++ int chip_id; ++ int regs_size; ++ int addr_width; ++ int addr_shift; ++}; ++ ++static const struct ks8995_chip_params ks8995_chip[] = { ++ [ks8995] = { ++ .name = "KS8995MA", ++ .family_id = FAMILY_KS8995, ++ .chip_id = KS8995_CHIP_ID, ++ .regs_size = KS8995_REGS_SIZE, ++ .addr_width = 8, ++ .addr_shift = 0, ++ }, ++ [ksz8864] = { ++ .name = "KSZ8864RMN", ++ .family_id = FAMILY_KS8995, ++ .chip_id = KSZ8864_CHIP_ID, ++ .regs_size = KSZ8864_REGS_SIZE, ++ .addr_width = 8, ++ .addr_shift = 0, ++ }, ++ [ksz8795] = { ++ .name = "KSZ8795CLX", ++ .family_id = FAMILY_KSZ8795, ++ .chip_id = KSZ8795_CHIP_ID, ++ .regs_size = KSZ8795_REGS_SIZE, ++ .addr_width = 12, ++ .addr_shift = 1, ++ }, ++}; ++ ++struct ks8995_switch { ++ struct spi_device *spi; ++ struct mutex lock; ++ struct gpio_desc *reset_gpio; ++ struct bin_attribute regs_attr; ++ const struct ks8995_chip_params *chip; ++ int revision_id; ++}; ++ ++static const struct spi_device_id ks8995_id[] = { ++ {"ks8995", ks8995}, ++ {"ksz8864", ksz8864}, ++ {"ksz8795", ksz8795}, ++ { } ++}; ++MODULE_DEVICE_TABLE(spi, ks8995_id); ++ ++static const struct of_device_id ks8895_spi_of_match[] = { ++ { .compatible = "micrel,ks8995" }, ++ { .compatible = "micrel,ksz8864" }, ++ { .compatible = "micrel,ksz8795" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, ks8895_spi_of_match); ++ ++static inline u8 get_chip_id(u8 val) ++{ ++ return (val >> ID1_CHIPID_S) & ID1_CHIPID_M; ++} ++ ++static inline u8 get_chip_rev(u8 val) ++{ ++ return (val >> ID1_REVISION_S) & ID1_REVISION_M; ++} ++ ++/* create_spi_cmd - create a chip specific SPI command header ++ * @ks: pointer to switch instance ++ * @cmd: SPI command for switch ++ * @address: register address for command ++ * ++ * Different chip families use different bit pattern to address the switches ++ * registers: ++ * ++ * KS8995: 8bit command + 8bit address ++ * KSZ8795: 3bit command + 12bit address + 1bit TR (?) ++ */ ++static inline __be16 create_spi_cmd(struct ks8995_switch *ks, int cmd, ++ unsigned address) ++{ ++ u16 result = cmd; ++ ++ /* make room for address (incl. address shift) */ ++ result <<= ks->chip->addr_width + ks->chip->addr_shift; ++ /* add address */ ++ result |= address << ks->chip->addr_shift; ++ /* SPI protocol needs big endian */ ++ return cpu_to_be16(result); ++} ++/* ------------------------------------------------------------------------ */ ++static int ks8995_read(struct ks8995_switch *ks, char *buf, ++ unsigned offset, size_t count) ++{ ++ __be16 cmd; ++ struct spi_transfer t[2]; ++ struct spi_message m; ++ int err; ++ ++ cmd = create_spi_cmd(ks, KS8995_CMD_READ, offset); ++ spi_message_init(&m); ++ ++ memset(&t, 0, sizeof(t)); ++ ++ t[0].tx_buf = &cmd; ++ t[0].len = sizeof(cmd); ++ spi_message_add_tail(&t[0], &m); ++ ++ t[1].rx_buf = buf; ++ t[1].len = count; ++ spi_message_add_tail(&t[1], &m); ++ ++ mutex_lock(&ks->lock); ++ err = spi_sync(ks->spi, &m); ++ mutex_unlock(&ks->lock); ++ ++ return err ? err : count; ++} ++ ++static int ks8995_write(struct ks8995_switch *ks, char *buf, ++ unsigned offset, size_t count) ++{ ++ __be16 cmd; ++ struct spi_transfer t[2]; ++ struct spi_message m; ++ int err; ++ ++ cmd = create_spi_cmd(ks, KS8995_CMD_WRITE, offset); ++ spi_message_init(&m); ++ ++ memset(&t, 0, sizeof(t)); ++ ++ t[0].tx_buf = &cmd; ++ t[0].len = sizeof(cmd); ++ spi_message_add_tail(&t[0], &m); ++ ++ t[1].tx_buf = buf; ++ t[1].len = count; ++ spi_message_add_tail(&t[1], &m); ++ ++ mutex_lock(&ks->lock); ++ err = spi_sync(ks->spi, &m); ++ mutex_unlock(&ks->lock); ++ ++ return err ? err : count; ++} ++ ++static inline int ks8995_read_reg(struct ks8995_switch *ks, u8 addr, u8 *buf) ++{ ++ return ks8995_read(ks, buf, addr, 1) != 1; ++} ++ ++static inline int ks8995_write_reg(struct ks8995_switch *ks, u8 addr, u8 val) ++{ ++ char buf = val; ++ ++ return ks8995_write(ks, &buf, addr, 1) != 1; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int ks8995_stop(struct ks8995_switch *ks) ++{ ++ return ks8995_write_reg(ks, KS8995_REG_ID1, 0); ++} ++ ++static int ks8995_start(struct ks8995_switch *ks) ++{ ++ return ks8995_write_reg(ks, KS8995_REG_ID1, 1); ++} ++ ++static int ks8995_reset(struct ks8995_switch *ks) ++{ ++ int err; ++ ++ err = ks8995_stop(ks); ++ if (err) ++ return err; ++ ++ udelay(KS8995_RESET_DELAY); ++ ++ return ks8995_start(ks); ++} ++ ++static ssize_t ks8995_registers_read(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) ++{ ++ struct device *dev; ++ struct ks8995_switch *ks8995; ++ ++ dev = kobj_to_dev(kobj); ++ ks8995 = dev_get_drvdata(dev); ++ ++ return ks8995_read(ks8995, buf, off, count); ++} ++ ++static ssize_t ks8995_registers_write(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) ++{ ++ struct device *dev; ++ struct ks8995_switch *ks8995; ++ ++ dev = kobj_to_dev(kobj); ++ ks8995 = dev_get_drvdata(dev); ++ ++ return ks8995_write(ks8995, buf, off, count); ++} ++ ++/* ks8995_get_revision - get chip revision ++ * @ks: pointer to switch instance ++ * ++ * Verify chip family and id and get chip revision. ++ */ ++static int ks8995_get_revision(struct ks8995_switch *ks) ++{ ++ int err; ++ u8 id0, id1, ksz8864_id; ++ ++ /* read family id */ ++ err = ks8995_read_reg(ks, KS8995_REG_ID0, &id0); ++ if (err) { ++ err = -EIO; ++ goto err_out; ++ } ++ ++ /* verify family id */ ++ if (id0 != ks->chip->family_id) { ++ dev_err(&ks->spi->dev, "chip family id mismatch: expected 0x%02x but 0x%02x read\n", ++ ks->chip->family_id, id0); ++ err = -ENODEV; ++ goto err_out; ++ } ++ ++ switch (ks->chip->family_id) { ++ case FAMILY_KS8995: ++ /* try reading chip id at CHIP ID1 */ ++ err = ks8995_read_reg(ks, KS8995_REG_ID1, &id1); ++ if (err) { ++ err = -EIO; ++ goto err_out; ++ } ++ ++ /* verify chip id */ ++ if ((get_chip_id(id1) == CHIPID_M) && ++ (get_chip_id(id1) == ks->chip->chip_id)) { ++ /* KS8995MA */ ++ ks->revision_id = get_chip_rev(id1); ++ } else if (get_chip_id(id1) != CHIPID_M) { ++ /* KSZ8864RMN */ ++ err = ks8995_read_reg(ks, KS8995_REG_ID1, &ksz8864_id); ++ if (err) { ++ err = -EIO; ++ goto err_out; ++ } ++ ++ if ((ksz8864_id & 0x80) && ++ (ks->chip->chip_id == KSZ8864_CHIP_ID)) { ++ ks->revision_id = get_chip_rev(id1); ++ } ++ ++ } else { ++ dev_err(&ks->spi->dev, "unsupported chip id for KS8995 family: 0x%02x\n", ++ id1); ++ err = -ENODEV; ++ } ++ break; ++ case FAMILY_KSZ8795: ++ /* try reading chip id at CHIP ID1 */ ++ err = ks8995_read_reg(ks, KS8995_REG_ID1, &id1); ++ if (err) { ++ err = -EIO; ++ goto err_out; ++ } ++ ++ if (get_chip_id(id1) == ks->chip->chip_id) { ++ ks->revision_id = get_chip_rev(id1); ++ } else { ++ dev_err(&ks->spi->dev, "unsupported chip id for KSZ8795 family: 0x%02x\n", ++ id1); ++ err = -ENODEV; ++ } ++ break; ++ default: ++ dev_err(&ks->spi->dev, "unsupported family id: 0x%02x\n", id0); ++ err = -ENODEV; ++ break; ++ } ++err_out: ++ return err; ++} ++ ++static const struct bin_attribute ks8995_registers_attr = { ++ .attr = { ++ .name = "registers", ++ .mode = 0600, ++ }, ++ .size = KS8995_REGS_SIZE, ++ .read = ks8995_registers_read, ++ .write = ks8995_registers_write, ++}; ++ ++/* ------------------------------------------------------------------------ */ ++static int ks8995_probe(struct spi_device *spi) ++{ ++ struct ks8995_switch *ks; ++ int err; ++ int variant = spi_get_device_id(spi)->driver_data; ++ ++ if (variant >= max_variant) { ++ dev_err(&spi->dev, "bad chip variant %d\n", variant); ++ return -ENODEV; ++ } ++ ++ ks = devm_kzalloc(&spi->dev, sizeof(*ks), GFP_KERNEL); ++ if (!ks) ++ return -ENOMEM; ++ ++ mutex_init(&ks->lock); ++ ks->spi = spi; ++ ks->chip = &ks8995_chip[variant]; ++ ++ ks->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", ++ GPIOD_OUT_HIGH); ++ err = PTR_ERR_OR_ZERO(ks->reset_gpio); ++ if (err) { ++ dev_err(&spi->dev, ++ "failed to get reset gpio: %d\n", err); ++ return err; ++ } ++ ++ err = gpiod_set_consumer_name(ks->reset_gpio, "switch-reset"); ++ if (err) ++ return err; ++ ++ /* de-assert switch reset */ ++ /* FIXME: this likely requires a delay */ ++ gpiod_set_value_cansleep(ks->reset_gpio, 0); ++ ++ spi_set_drvdata(spi, ks); ++ ++ spi->mode = SPI_MODE_0; ++ spi->bits_per_word = 8; ++ err = spi_setup(spi); ++ if (err) { ++ dev_err(&spi->dev, "spi_setup failed, err=%d\n", err); ++ return err; ++ } ++ ++ err = ks8995_get_revision(ks); ++ if (err) ++ return err; ++ ++ memcpy(&ks->regs_attr, &ks8995_registers_attr, sizeof(ks->regs_attr)); ++ ks->regs_attr.size = ks->chip->regs_size; ++ ++ err = ks8995_reset(ks); ++ if (err) ++ return err; ++ ++ sysfs_attr_init(&ks->regs_attr.attr); ++ err = sysfs_create_bin_file(&spi->dev.kobj, &ks->regs_attr); ++ if (err) { ++ dev_err(&spi->dev, "unable to create sysfs file, err=%d\n", ++ err); ++ return err; ++ } ++ ++ dev_info(&spi->dev, "%s device found, Chip ID:%x, Revision:%x\n", ++ ks->chip->name, ks->chip->chip_id, ks->revision_id); ++ ++ return 0; ++} ++ ++static void ks8995_remove(struct spi_device *spi) ++{ ++ struct ks8995_switch *ks = spi_get_drvdata(spi); ++ ++ sysfs_remove_bin_file(&spi->dev.kobj, &ks->regs_attr); ++ ++ /* assert reset */ ++ gpiod_set_value_cansleep(ks->reset_gpio, 1); ++} ++ ++/* ------------------------------------------------------------------------ */ ++static struct spi_driver ks8995_driver = { ++ .driver = { ++ .name = "spi-ks8995", ++ .of_match_table = ks8895_spi_of_match, ++ }, ++ .probe = ks8995_probe, ++ .remove = ks8995_remove, ++ .id_table = ks8995_id, ++}; ++ ++module_spi_driver(ks8995_driver); ++ ++MODULE_DESCRIPTION(DRV_DESC); ++MODULE_VERSION(DRV_VERSION); ++MODULE_AUTHOR("Gabor Juhos "); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/net/phy/spi_ks8995.c ++++ /dev/null +@@ -1,506 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * SPI driver for Micrel/Kendin KS8995M and KSZ8864RMN ethernet switches +- * +- * Copyright (C) 2008 Gabor Juhos +- * +- * This file was based on: drivers/spi/at25.c +- * Copyright (C) 2006 David Brownell +- */ +- +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#define DRV_VERSION "0.1.1" +-#define DRV_DESC "Micrel KS8995 Ethernet switch SPI driver" +- +-/* ------------------------------------------------------------------------ */ +- +-#define KS8995_REG_ID0 0x00 /* Chip ID0 */ +-#define KS8995_REG_ID1 0x01 /* Chip ID1 */ +- +-#define KS8995_REG_GC0 0x02 /* Global Control 0 */ +-#define KS8995_REG_GC1 0x03 /* Global Control 1 */ +-#define KS8995_REG_GC2 0x04 /* Global Control 2 */ +-#define KS8995_REG_GC3 0x05 /* Global Control 3 */ +-#define KS8995_REG_GC4 0x06 /* Global Control 4 */ +-#define KS8995_REG_GC5 0x07 /* Global Control 5 */ +-#define KS8995_REG_GC6 0x08 /* Global Control 6 */ +-#define KS8995_REG_GC7 0x09 /* Global Control 7 */ +-#define KS8995_REG_GC8 0x0a /* Global Control 8 */ +-#define KS8995_REG_GC9 0x0b /* Global Control 9 */ +- +-#define KS8995_REG_PC(p, r) ((0x10 * p) + r) /* Port Control */ +-#define KS8995_REG_PS(p, r) ((0x10 * p) + r + 0xe) /* Port Status */ +- +-#define KS8995_REG_TPC0 0x60 /* TOS Priority Control 0 */ +-#define KS8995_REG_TPC1 0x61 /* TOS Priority Control 1 */ +-#define KS8995_REG_TPC2 0x62 /* TOS Priority Control 2 */ +-#define KS8995_REG_TPC3 0x63 /* TOS Priority Control 3 */ +-#define KS8995_REG_TPC4 0x64 /* TOS Priority Control 4 */ +-#define KS8995_REG_TPC5 0x65 /* TOS Priority Control 5 */ +-#define KS8995_REG_TPC6 0x66 /* TOS Priority Control 6 */ +-#define KS8995_REG_TPC7 0x67 /* TOS Priority Control 7 */ +- +-#define KS8995_REG_MAC0 0x68 /* MAC address 0 */ +-#define KS8995_REG_MAC1 0x69 /* MAC address 1 */ +-#define KS8995_REG_MAC2 0x6a /* MAC address 2 */ +-#define KS8995_REG_MAC3 0x6b /* MAC address 3 */ +-#define KS8995_REG_MAC4 0x6c /* MAC address 4 */ +-#define KS8995_REG_MAC5 0x6d /* MAC address 5 */ +- +-#define KS8995_REG_IAC0 0x6e /* Indirect Access Control 0 */ +-#define KS8995_REG_IAC1 0x6f /* Indirect Access Control 0 */ +-#define KS8995_REG_IAD7 0x70 /* Indirect Access Data 7 */ +-#define KS8995_REG_IAD6 0x71 /* Indirect Access Data 6 */ +-#define KS8995_REG_IAD5 0x72 /* Indirect Access Data 5 */ +-#define KS8995_REG_IAD4 0x73 /* Indirect Access Data 4 */ +-#define KS8995_REG_IAD3 0x74 /* Indirect Access Data 3 */ +-#define KS8995_REG_IAD2 0x75 /* Indirect Access Data 2 */ +-#define KS8995_REG_IAD1 0x76 /* Indirect Access Data 1 */ +-#define KS8995_REG_IAD0 0x77 /* Indirect Access Data 0 */ +- +-#define KSZ8864_REG_ID1 0xfe /* Chip ID in bit 7 */ +- +-#define KS8995_REGS_SIZE 0x80 +-#define KSZ8864_REGS_SIZE 0x100 +-#define KSZ8795_REGS_SIZE 0x100 +- +-#define ID1_CHIPID_M 0xf +-#define ID1_CHIPID_S 4 +-#define ID1_REVISION_M 0x7 +-#define ID1_REVISION_S 1 +-#define ID1_START_SW 1 /* start the switch */ +- +-#define FAMILY_KS8995 0x95 +-#define FAMILY_KSZ8795 0x87 +-#define CHIPID_M 0 +-#define KS8995_CHIP_ID 0x00 +-#define KSZ8864_CHIP_ID 0x01 +-#define KSZ8795_CHIP_ID 0x09 +- +-#define KS8995_CMD_WRITE 0x02U +-#define KS8995_CMD_READ 0x03U +- +-#define KS8995_RESET_DELAY 10 /* usec */ +- +-enum ks8995_chip_variant { +- ks8995, +- ksz8864, +- ksz8795, +- max_variant +-}; +- +-struct ks8995_chip_params { +- char *name; +- int family_id; +- int chip_id; +- int regs_size; +- int addr_width; +- int addr_shift; +-}; +- +-static const struct ks8995_chip_params ks8995_chip[] = { +- [ks8995] = { +- .name = "KS8995MA", +- .family_id = FAMILY_KS8995, +- .chip_id = KS8995_CHIP_ID, +- .regs_size = KS8995_REGS_SIZE, +- .addr_width = 8, +- .addr_shift = 0, +- }, +- [ksz8864] = { +- .name = "KSZ8864RMN", +- .family_id = FAMILY_KS8995, +- .chip_id = KSZ8864_CHIP_ID, +- .regs_size = KSZ8864_REGS_SIZE, +- .addr_width = 8, +- .addr_shift = 0, +- }, +- [ksz8795] = { +- .name = "KSZ8795CLX", +- .family_id = FAMILY_KSZ8795, +- .chip_id = KSZ8795_CHIP_ID, +- .regs_size = KSZ8795_REGS_SIZE, +- .addr_width = 12, +- .addr_shift = 1, +- }, +-}; +- +-struct ks8995_switch { +- struct spi_device *spi; +- struct mutex lock; +- struct gpio_desc *reset_gpio; +- struct bin_attribute regs_attr; +- const struct ks8995_chip_params *chip; +- int revision_id; +-}; +- +-static const struct spi_device_id ks8995_id[] = { +- {"ks8995", ks8995}, +- {"ksz8864", ksz8864}, +- {"ksz8795", ksz8795}, +- { } +-}; +-MODULE_DEVICE_TABLE(spi, ks8995_id); +- +-static const struct of_device_id ks8895_spi_of_match[] = { +- { .compatible = "micrel,ks8995" }, +- { .compatible = "micrel,ksz8864" }, +- { .compatible = "micrel,ksz8795" }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, ks8895_spi_of_match); +- +-static inline u8 get_chip_id(u8 val) +-{ +- return (val >> ID1_CHIPID_S) & ID1_CHIPID_M; +-} +- +-static inline u8 get_chip_rev(u8 val) +-{ +- return (val >> ID1_REVISION_S) & ID1_REVISION_M; +-} +- +-/* create_spi_cmd - create a chip specific SPI command header +- * @ks: pointer to switch instance +- * @cmd: SPI command for switch +- * @address: register address for command +- * +- * Different chip families use different bit pattern to address the switches +- * registers: +- * +- * KS8995: 8bit command + 8bit address +- * KSZ8795: 3bit command + 12bit address + 1bit TR (?) +- */ +-static inline __be16 create_spi_cmd(struct ks8995_switch *ks, int cmd, +- unsigned address) +-{ +- u16 result = cmd; +- +- /* make room for address (incl. address shift) */ +- result <<= ks->chip->addr_width + ks->chip->addr_shift; +- /* add address */ +- result |= address << ks->chip->addr_shift; +- /* SPI protocol needs big endian */ +- return cpu_to_be16(result); +-} +-/* ------------------------------------------------------------------------ */ +-static int ks8995_read(struct ks8995_switch *ks, char *buf, +- unsigned offset, size_t count) +-{ +- __be16 cmd; +- struct spi_transfer t[2]; +- struct spi_message m; +- int err; +- +- cmd = create_spi_cmd(ks, KS8995_CMD_READ, offset); +- spi_message_init(&m); +- +- memset(&t, 0, sizeof(t)); +- +- t[0].tx_buf = &cmd; +- t[0].len = sizeof(cmd); +- spi_message_add_tail(&t[0], &m); +- +- t[1].rx_buf = buf; +- t[1].len = count; +- spi_message_add_tail(&t[1], &m); +- +- mutex_lock(&ks->lock); +- err = spi_sync(ks->spi, &m); +- mutex_unlock(&ks->lock); +- +- return err ? err : count; +-} +- +-static int ks8995_write(struct ks8995_switch *ks, char *buf, +- unsigned offset, size_t count) +-{ +- __be16 cmd; +- struct spi_transfer t[2]; +- struct spi_message m; +- int err; +- +- cmd = create_spi_cmd(ks, KS8995_CMD_WRITE, offset); +- spi_message_init(&m); +- +- memset(&t, 0, sizeof(t)); +- +- t[0].tx_buf = &cmd; +- t[0].len = sizeof(cmd); +- spi_message_add_tail(&t[0], &m); +- +- t[1].tx_buf = buf; +- t[1].len = count; +- spi_message_add_tail(&t[1], &m); +- +- mutex_lock(&ks->lock); +- err = spi_sync(ks->spi, &m); +- mutex_unlock(&ks->lock); +- +- return err ? err : count; +-} +- +-static inline int ks8995_read_reg(struct ks8995_switch *ks, u8 addr, u8 *buf) +-{ +- return ks8995_read(ks, buf, addr, 1) != 1; +-} +- +-static inline int ks8995_write_reg(struct ks8995_switch *ks, u8 addr, u8 val) +-{ +- char buf = val; +- +- return ks8995_write(ks, &buf, addr, 1) != 1; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int ks8995_stop(struct ks8995_switch *ks) +-{ +- return ks8995_write_reg(ks, KS8995_REG_ID1, 0); +-} +- +-static int ks8995_start(struct ks8995_switch *ks) +-{ +- return ks8995_write_reg(ks, KS8995_REG_ID1, 1); +-} +- +-static int ks8995_reset(struct ks8995_switch *ks) +-{ +- int err; +- +- err = ks8995_stop(ks); +- if (err) +- return err; +- +- udelay(KS8995_RESET_DELAY); +- +- return ks8995_start(ks); +-} +- +-static ssize_t ks8995_registers_read(struct file *filp, struct kobject *kobj, +- struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) +-{ +- struct device *dev; +- struct ks8995_switch *ks8995; +- +- dev = kobj_to_dev(kobj); +- ks8995 = dev_get_drvdata(dev); +- +- return ks8995_read(ks8995, buf, off, count); +-} +- +-static ssize_t ks8995_registers_write(struct file *filp, struct kobject *kobj, +- struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) +-{ +- struct device *dev; +- struct ks8995_switch *ks8995; +- +- dev = kobj_to_dev(kobj); +- ks8995 = dev_get_drvdata(dev); +- +- return ks8995_write(ks8995, buf, off, count); +-} +- +-/* ks8995_get_revision - get chip revision +- * @ks: pointer to switch instance +- * +- * Verify chip family and id and get chip revision. +- */ +-static int ks8995_get_revision(struct ks8995_switch *ks) +-{ +- int err; +- u8 id0, id1, ksz8864_id; +- +- /* read family id */ +- err = ks8995_read_reg(ks, KS8995_REG_ID0, &id0); +- if (err) { +- err = -EIO; +- goto err_out; +- } +- +- /* verify family id */ +- if (id0 != ks->chip->family_id) { +- dev_err(&ks->spi->dev, "chip family id mismatch: expected 0x%02x but 0x%02x read\n", +- ks->chip->family_id, id0); +- err = -ENODEV; +- goto err_out; +- } +- +- switch (ks->chip->family_id) { +- case FAMILY_KS8995: +- /* try reading chip id at CHIP ID1 */ +- err = ks8995_read_reg(ks, KS8995_REG_ID1, &id1); +- if (err) { +- err = -EIO; +- goto err_out; +- } +- +- /* verify chip id */ +- if ((get_chip_id(id1) == CHIPID_M) && +- (get_chip_id(id1) == ks->chip->chip_id)) { +- /* KS8995MA */ +- ks->revision_id = get_chip_rev(id1); +- } else if (get_chip_id(id1) != CHIPID_M) { +- /* KSZ8864RMN */ +- err = ks8995_read_reg(ks, KS8995_REG_ID1, &ksz8864_id); +- if (err) { +- err = -EIO; +- goto err_out; +- } +- +- if ((ksz8864_id & 0x80) && +- (ks->chip->chip_id == KSZ8864_CHIP_ID)) { +- ks->revision_id = get_chip_rev(id1); +- } +- +- } else { +- dev_err(&ks->spi->dev, "unsupported chip id for KS8995 family: 0x%02x\n", +- id1); +- err = -ENODEV; +- } +- break; +- case FAMILY_KSZ8795: +- /* try reading chip id at CHIP ID1 */ +- err = ks8995_read_reg(ks, KS8995_REG_ID1, &id1); +- if (err) { +- err = -EIO; +- goto err_out; +- } +- +- if (get_chip_id(id1) == ks->chip->chip_id) { +- ks->revision_id = get_chip_rev(id1); +- } else { +- dev_err(&ks->spi->dev, "unsupported chip id for KSZ8795 family: 0x%02x\n", +- id1); +- err = -ENODEV; +- } +- break; +- default: +- dev_err(&ks->spi->dev, "unsupported family id: 0x%02x\n", id0); +- err = -ENODEV; +- break; +- } +-err_out: +- return err; +-} +- +-static const struct bin_attribute ks8995_registers_attr = { +- .attr = { +- .name = "registers", +- .mode = 0600, +- }, +- .size = KS8995_REGS_SIZE, +- .read = ks8995_registers_read, +- .write = ks8995_registers_write, +-}; +- +-/* ------------------------------------------------------------------------ */ +-static int ks8995_probe(struct spi_device *spi) +-{ +- struct ks8995_switch *ks; +- int err; +- int variant = spi_get_device_id(spi)->driver_data; +- +- if (variant >= max_variant) { +- dev_err(&spi->dev, "bad chip variant %d\n", variant); +- return -ENODEV; +- } +- +- ks = devm_kzalloc(&spi->dev, sizeof(*ks), GFP_KERNEL); +- if (!ks) +- return -ENOMEM; +- +- mutex_init(&ks->lock); +- ks->spi = spi; +- ks->chip = &ks8995_chip[variant]; +- +- ks->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", +- GPIOD_OUT_HIGH); +- err = PTR_ERR_OR_ZERO(ks->reset_gpio); +- if (err) { +- dev_err(&spi->dev, +- "failed to get reset gpio: %d\n", err); +- return err; +- } +- +- err = gpiod_set_consumer_name(ks->reset_gpio, "switch-reset"); +- if (err) +- return err; +- +- /* de-assert switch reset */ +- /* FIXME: this likely requires a delay */ +- gpiod_set_value_cansleep(ks->reset_gpio, 0); +- +- spi_set_drvdata(spi, ks); +- +- spi->mode = SPI_MODE_0; +- spi->bits_per_word = 8; +- err = spi_setup(spi); +- if (err) { +- dev_err(&spi->dev, "spi_setup failed, err=%d\n", err); +- return err; +- } +- +- err = ks8995_get_revision(ks); +- if (err) +- return err; +- +- memcpy(&ks->regs_attr, &ks8995_registers_attr, sizeof(ks->regs_attr)); +- ks->regs_attr.size = ks->chip->regs_size; +- +- err = ks8995_reset(ks); +- if (err) +- return err; +- +- sysfs_attr_init(&ks->regs_attr.attr); +- err = sysfs_create_bin_file(&spi->dev.kobj, &ks->regs_attr); +- if (err) { +- dev_err(&spi->dev, "unable to create sysfs file, err=%d\n", +- err); +- return err; +- } +- +- dev_info(&spi->dev, "%s device found, Chip ID:%x, Revision:%x\n", +- ks->chip->name, ks->chip->chip_id, ks->revision_id); +- +- return 0; +-} +- +-static void ks8995_remove(struct spi_device *spi) +-{ +- struct ks8995_switch *ks = spi_get_drvdata(spi); +- +- sysfs_remove_bin_file(&spi->dev.kobj, &ks->regs_attr); +- +- /* assert reset */ +- gpiod_set_value_cansleep(ks->reset_gpio, 1); +-} +- +-/* ------------------------------------------------------------------------ */ +-static struct spi_driver ks8995_driver = { +- .driver = { +- .name = "spi-ks8995", +- .of_match_table = ks8895_spi_of_match, +- }, +- .probe = ks8995_probe, +- .remove = ks8995_remove, +- .id_table = ks8995_id, +-}; +- +-module_spi_driver(ks8995_driver); +- +-MODULE_DESCRIPTION(DRV_DESC); +-MODULE_VERSION(DRV_VERSION); +-MODULE_AUTHOR("Gabor Juhos "); +-MODULE_LICENSE("GPL v2"); diff --git a/target/linux/generic/backport-6.12/783-02-v6.18-net-dsa-ks8995-Add-proper-RESET-delay.patch b/target/linux/generic/backport-6.12/783-02-v6.18-net-dsa-ks8995-Add-proper-RESET-delay.patch new file mode 100644 index 00000000000..3b0b731d4bd --- /dev/null +++ b/target/linux/generic/backport-6.12/783-02-v6.18-net-dsa-ks8995-Add-proper-RESET-delay.patch @@ -0,0 +1,40 @@ +From 7367bfc8b34d7ad5b0c06e19fe24533db22ed8ea Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 13 Aug 2025 23:43:04 +0200 +Subject: [PATCH 2/4] net: dsa: ks8995: Add proper RESET delay + +According to the datasheet we need to wait 100us before accessing +any registers in the KS8995 after a reset de-assertion. + +Add this delay, if and only if we obtained a GPIO descriptor, +otherwise it is just a pointless delay. + +Signed-off-by: Linus Walleij +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250813-ks8995-to-dsa-v1-2-75c359ede3a5@linaro.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/ks8995.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/ks8995.c ++++ b/drivers/net/dsa/ks8995.c +@@ -438,9 +438,15 @@ static int ks8995_probe(struct spi_devic + if (err) + return err; + +- /* de-assert switch reset */ +- /* FIXME: this likely requires a delay */ +- gpiod_set_value_cansleep(ks->reset_gpio, 0); ++ if (ks->reset_gpio) { ++ /* ++ * If a reset line was obtained, wait for 100us after ++ * de-asserting RESET before accessing any registers, see ++ * the KS8995MA datasheet, page 44. ++ */ ++ gpiod_set_value_cansleep(ks->reset_gpio, 0); ++ udelay(100); ++ } + + spi_set_drvdata(spi, ks); + diff --git a/target/linux/generic/backport-6.12/783-03-v6.18-net-dsa-ks8995-Delete-sysfs-register-access.patch b/target/linux/generic/backport-6.12/783-03-v6.18-net-dsa-ks8995-Delete-sysfs-register-access.patch new file mode 100644 index 00000000000..78b9141ed75 --- /dev/null +++ b/target/linux/generic/backport-6.12/783-03-v6.18-net-dsa-ks8995-Delete-sysfs-register-access.patch @@ -0,0 +1,109 @@ +From 0e8d3f3623a45f69b6641ff9921ecd0c5afaa4ca Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 13 Aug 2025 23:43:05 +0200 +Subject: [PATCH 3/4] net: dsa: ks8995: Delete sysfs register access + +There is some sysfs file to read and write registers randomly +in the ks8995 driver. + +The contemporary way to achieve the same thing is to implement +regmap abstractions, if needed. Delete this and implement +regmap later if we want to be able to inspect individual registers. + +Signed-off-by: Linus Walleij +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250813-ks8995-to-dsa-v1-3-75c359ede3a5@linaro.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/ks8995.c | 48 ---------------------------------------- + 1 file changed, 48 deletions(-) + +--- a/drivers/net/dsa/ks8995.c ++++ b/drivers/net/dsa/ks8995.c +@@ -140,7 +140,6 @@ struct ks8995_switch { + struct spi_device *spi; + struct mutex lock; + struct gpio_desc *reset_gpio; +- struct bin_attribute regs_attr; + const struct ks8995_chip_params *chip; + int revision_id; + }; +@@ -288,30 +287,6 @@ static int ks8995_reset(struct ks8995_sw + return ks8995_start(ks); + } + +-static ssize_t ks8995_registers_read(struct file *filp, struct kobject *kobj, +- struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) +-{ +- struct device *dev; +- struct ks8995_switch *ks8995; +- +- dev = kobj_to_dev(kobj); +- ks8995 = dev_get_drvdata(dev); +- +- return ks8995_read(ks8995, buf, off, count); +-} +- +-static ssize_t ks8995_registers_write(struct file *filp, struct kobject *kobj, +- struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) +-{ +- struct device *dev; +- struct ks8995_switch *ks8995; +- +- dev = kobj_to_dev(kobj); +- ks8995 = dev_get_drvdata(dev); +- +- return ks8995_write(ks8995, buf, off, count); +-} +- + /* ks8995_get_revision - get chip revision + * @ks: pointer to switch instance + * +@@ -395,16 +370,6 @@ err_out: + return err; + } + +-static const struct bin_attribute ks8995_registers_attr = { +- .attr = { +- .name = "registers", +- .mode = 0600, +- }, +- .size = KS8995_REGS_SIZE, +- .read = ks8995_registers_read, +- .write = ks8995_registers_write, +-}; +- + /* ------------------------------------------------------------------------ */ + static int ks8995_probe(struct spi_device *spi) + { +@@ -462,21 +427,10 @@ static int ks8995_probe(struct spi_devic + if (err) + return err; + +- memcpy(&ks->regs_attr, &ks8995_registers_attr, sizeof(ks->regs_attr)); +- ks->regs_attr.size = ks->chip->regs_size; +- + err = ks8995_reset(ks); + if (err) + return err; + +- sysfs_attr_init(&ks->regs_attr.attr); +- err = sysfs_create_bin_file(&spi->dev.kobj, &ks->regs_attr); +- if (err) { +- dev_err(&spi->dev, "unable to create sysfs file, err=%d\n", +- err); +- return err; +- } +- + dev_info(&spi->dev, "%s device found, Chip ID:%x, Revision:%x\n", + ks->chip->name, ks->chip->chip_id, ks->revision_id); + +@@ -487,8 +441,6 @@ static void ks8995_remove(struct spi_dev + { + struct ks8995_switch *ks = spi_get_drvdata(spi); + +- sysfs_remove_bin_file(&spi->dev.kobj, &ks->regs_attr); +- + /* assert reset */ + gpiod_set_value_cansleep(ks->reset_gpio, 1); + } diff --git a/target/linux/generic/backport-6.12/783-04-v6.18-net-dsa-ks8995-Add-basic-switch-set-up.patch b/target/linux/generic/backport-6.12/783-04-v6.18-net-dsa-ks8995-Add-basic-switch-set-up.patch new file mode 100644 index 00000000000..3454e113baa --- /dev/null +++ b/target/linux/generic/backport-6.12/783-04-v6.18-net-dsa-ks8995-Add-basic-switch-set-up.patch @@ -0,0 +1,546 @@ +From 4b486b63e07313e1e3807f9d5e6636aa564e0fbc Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Wed, 13 Aug 2025 23:43:06 +0200 +Subject: [PATCH 4/4] net: dsa: ks8995: Add basic switch set-up + +We start to extend the KS8995 driver by simply registering it +as a DSA device and implementing a few switch callbacks for +STP set-up and such to begin with. + +No special tags or other advanced stuff: we use DSA_TAG_NONE +and rely on the default set-up in the switch with the special +DSA tags turned off. This makes the switch wire up properly +to all its PHY's and simple bridge traffic works properly. + +After this the bridge DT bindings are respected, ports and their +PHYs get connected to the switch and react appropriately through +the phylib when cables are plugged in etc. + +Tested like this in a hacky OpenWrt image: + +Bring up conduit interface manually: +ixp4xx_eth c8009000.ethernet eth0: eth0: link up, + speed 100 Mb/s, full duplex + +spi-ks8995 spi0.0: enable port 0 +spi-ks8995 spi0.0: set KS8995_REG_PC2 for port 0 to 06 +spi-ks8995 spi0.0 lan1: configuring for phy/mii link mode +spi-ks8995 spi0.0 lan1: Link is Up - 100Mbps/Full - flow control rx/tx + +PING 169.254.1.1 (169.254.1.1): 56 data bytes +64 bytes from 169.254.1.1: seq=0 ttl=64 time=1.629 ms +64 bytes from 169.254.1.1: seq=1 ttl=64 time=0.951 ms + +I also tested SSH from the device to the host and it works fine. + +It also works fine to ping the device from the host and to SSH +into the device from the host. + +This brings the ks8995 driver to a reasonable state where it can +be used from the current device tree bindings and the existing +device trees in the kernel. + +Signed-off-by: Linus Walleij +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250813-ks8995-to-dsa-v1-4-75c359ede3a5@linaro.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/Kconfig | 1 + + drivers/net/dsa/ks8995.c | 398 ++++++++++++++++++++++++++++++++++++++- + 2 files changed, 396 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -103,6 +103,7 @@ config NET_DSA_RZN1_A5PSW + config NET_DSA_KS8995 + tristate "Micrel KS8995 family 5-ports 10/100 Ethernet switches" + depends on SPI ++ select NET_DSA_TAG_NONE + help + This driver supports the Micrel KS8995 family of 10/100 Mbit ethernet + switches, managed over SPI. +--- a/drivers/net/dsa/ks8995.c ++++ b/drivers/net/dsa/ks8995.c +@@ -3,6 +3,7 @@ + * SPI driver for Micrel/Kendin KS8995M and KSZ8864RMN ethernet switches + * + * Copyright (C) 2008 Gabor Juhos ++ * Copyright (C) 2025 Linus Walleij + * + * This file was based on: drivers/spi/at25.c + * Copyright (C) 2006 David Brownell +@@ -10,6 +11,9 @@ + + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + ++#include ++#include ++#include + #include + #include + #include +@@ -17,8 +21,8 @@ + #include + #include + #include +- + #include ++#include + + #define DRV_VERSION "0.1.1" + #define DRV_DESC "Micrel KS8995 Ethernet switch SPI driver" +@@ -29,18 +33,59 @@ + #define KS8995_REG_ID1 0x01 /* Chip ID1 */ + + #define KS8995_REG_GC0 0x02 /* Global Control 0 */ ++ ++#define KS8995_GC0_P5_PHY BIT(3) /* Port 5 PHY enabled */ ++ + #define KS8995_REG_GC1 0x03 /* Global Control 1 */ + #define KS8995_REG_GC2 0x04 /* Global Control 2 */ ++ ++#define KS8995_GC2_HUGE BIT(2) /* Huge packet support */ ++#define KS8995_GC2_LEGAL BIT(1) /* Legal size override */ ++ + #define KS8995_REG_GC3 0x05 /* Global Control 3 */ + #define KS8995_REG_GC4 0x06 /* Global Control 4 */ ++ ++#define KS8995_GC4_10BT BIT(4) /* Force switch to 10Mbit */ ++#define KS8995_GC4_MII_FLOW BIT(5) /* MII full-duplex flow control enable */ ++#define KS8995_GC4_MII_HD BIT(6) /* MII half-duplex mode enable */ ++ + #define KS8995_REG_GC5 0x07 /* Global Control 5 */ + #define KS8995_REG_GC6 0x08 /* Global Control 6 */ + #define KS8995_REG_GC7 0x09 /* Global Control 7 */ + #define KS8995_REG_GC8 0x0a /* Global Control 8 */ + #define KS8995_REG_GC9 0x0b /* Global Control 9 */ + +-#define KS8995_REG_PC(p, r) ((0x10 * p) + r) /* Port Control */ +-#define KS8995_REG_PS(p, r) ((0x10 * p) + r + 0xe) /* Port Status */ ++#define KS8995_GC9_SPECIAL BIT(0) /* Special tagging mode (DSA) */ ++ ++/* In DSA the ports 1-4 are numbered 0-3 and the CPU port is port 4 */ ++#define KS8995_REG_PC(p, r) (0x10 + (0x10 * (p)) + (r)) /* Port Control */ ++#define KS8995_REG_PS(p, r) (0x1e + (0x10 * (p)) + (r)) /* Port Status */ ++ ++#define KS8995_REG_PC0 0x00 /* Port Control 0 */ ++#define KS8995_REG_PC1 0x01 /* Port Control 1 */ ++#define KS8995_REG_PC2 0x02 /* Port Control 2 */ ++#define KS8995_REG_PC3 0x03 /* Port Control 3 */ ++#define KS8995_REG_PC4 0x04 /* Port Control 4 */ ++#define KS8995_REG_PC5 0x05 /* Port Control 5 */ ++#define KS8995_REG_PC6 0x06 /* Port Control 6 */ ++#define KS8995_REG_PC7 0x07 /* Port Control 7 */ ++#define KS8995_REG_PC8 0x08 /* Port Control 8 */ ++#define KS8995_REG_PC9 0x09 /* Port Control 9 */ ++#define KS8995_REG_PC10 0x0a /* Port Control 10 */ ++#define KS8995_REG_PC11 0x0b /* Port Control 11 */ ++#define KS8995_REG_PC12 0x0c /* Port Control 12 */ ++#define KS8995_REG_PC13 0x0d /* Port Control 13 */ ++ ++#define KS8995_PC0_TAG_INS BIT(2) /* Enable tag insertion on port */ ++#define KS8995_PC0_TAG_REM BIT(1) /* Enable tag removal on port */ ++#define KS8995_PC0_PRIO_EN BIT(0) /* Enable priority handling */ ++ ++#define KS8995_PC2_TXEN BIT(2) /* Enable TX on port */ ++#define KS8995_PC2_RXEN BIT(1) /* Enable RX on port */ ++#define KS8995_PC2_LEARN_DIS BIT(0) /* Disable learning on port */ ++ ++#define KS8995_PC13_TXDIS BIT(6) /* Disable transmitter */ ++#define KS8995_PC13_PWDN BIT(3) /* Power down */ + + #define KS8995_REG_TPC0 0x60 /* TOS Priority Control 0 */ + #define KS8995_REG_TPC1 0x61 /* TOS Priority Control 1 */ +@@ -91,6 +136,8 @@ + #define KS8995_CMD_WRITE 0x02U + #define KS8995_CMD_READ 0x03U + ++#define KS8995_CPU_PORT 4 ++#define KS8995_NUM_PORTS 5 /* 5 ports including the CPU port */ + #define KS8995_RESET_DELAY 10 /* usec */ + + enum ks8995_chip_variant { +@@ -138,10 +185,13 @@ static const struct ks8995_chip_params k + + struct ks8995_switch { + struct spi_device *spi; ++ struct device *dev; ++ struct dsa_switch *ds; + struct mutex lock; + struct gpio_desc *reset_gpio; + const struct ks8995_chip_params *chip; + int revision_id; ++ unsigned int max_mtu[KS8995_NUM_PORTS]; + }; + + static const struct spi_device_id ks8995_id[] = { +@@ -370,6 +420,327 @@ err_out: + return err; + } + ++static int ks8995_check_config(struct ks8995_switch *ks) ++{ ++ int ret; ++ u8 val; ++ ++ ret = ks8995_read_reg(ks, KS8995_REG_GC0, &val); ++ if (ret) { ++ dev_err(ks->dev, "failed to read KS8995_REG_GC0\n"); ++ return ret; ++ } ++ ++ dev_dbg(ks->dev, "port 5 PHY %senabled\n", ++ (val & KS8995_GC0_P5_PHY) ? "" : "not "); ++ ++ val |= KS8995_GC0_P5_PHY; ++ ret = ks8995_write_reg(ks, KS8995_REG_GC0, val); ++ if (ret) ++ dev_err(ks->dev, "failed to set KS8995_REG_GC0\n"); ++ ++ dev_dbg(ks->dev, "set KS8995_REG_GC0 to 0x%02x\n", val); ++ ++ return 0; ++} ++ ++static void ++ks8995_mac_config(struct phylink_config *config, unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++} ++ ++static void ++ks8995_mac_link_up(struct phylink_config *config, struct phy_device *phydev, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ struct dsa_port *dp = dsa_phylink_to_port(config); ++ struct ks8995_switch *ks = dp->ds->priv; ++ int port = dp->index; ++ int ret; ++ u8 val; ++ ++ /* Allow forcing the mode on the fixed CPU port, no autonegotiation. ++ * We assume autonegotiation works on the PHY-facing ports. ++ */ ++ if (port != KS8995_CPU_PORT) ++ return; ++ ++ dev_dbg(ks->dev, "MAC link up on CPU port (%d)\n", port); ++ ++ ret = ks8995_read_reg(ks, KS8995_REG_GC4, &val); ++ if (ret) { ++ dev_err(ks->dev, "failed to read KS8995_REG_GC4\n"); ++ return; ++ } ++ ++ /* Conjure port config */ ++ switch (speed) { ++ case SPEED_10: ++ dev_dbg(ks->dev, "set switch MII to 100Mbit mode\n"); ++ val |= KS8995_GC4_10BT; ++ break; ++ case SPEED_100: ++ default: ++ dev_dbg(ks->dev, "set switch MII to 100Mbit mode\n"); ++ val &= ~KS8995_GC4_10BT; ++ break; ++ } ++ ++ if (duplex == DUPLEX_HALF) { ++ dev_dbg(ks->dev, "set switch MII to half duplex\n"); ++ val |= KS8995_GC4_MII_HD; ++ } else { ++ dev_dbg(ks->dev, "set switch MII to full duplex\n"); ++ val &= ~KS8995_GC4_MII_HD; ++ } ++ ++ dev_dbg(ks->dev, "set KS8995_REG_GC4 to %02x\n", val); ++ ++ /* Enable the CPU port */ ++ ret = ks8995_write_reg(ks, KS8995_REG_GC4, val); ++ if (ret) ++ dev_err(ks->dev, "failed to set KS8995_REG_GC4\n"); ++} ++ ++static void ++ks8995_mac_link_down(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct dsa_port *dp = dsa_phylink_to_port(config); ++ struct ks8995_switch *ks = dp->ds->priv; ++ int port = dp->index; ++ ++ if (port != KS8995_CPU_PORT) ++ return; ++ ++ dev_dbg(ks->dev, "MAC link down on CPU port (%d)\n", port); ++ ++ /* Disable the CPU port */ ++} ++ ++static const struct phylink_mac_ops ks8995_phylink_mac_ops = { ++ .mac_config = ks8995_mac_config, ++ .mac_link_up = ks8995_mac_link_up, ++ .mac_link_down = ks8995_mac_link_down, ++}; ++ ++static enum ++dsa_tag_protocol ks8995_get_tag_protocol(struct dsa_switch *ds, ++ int port, ++ enum dsa_tag_protocol mp) ++{ ++ /* This switch actually uses the 6 byte KS8995 protocol */ ++ return DSA_TAG_PROTO_NONE; ++} ++ ++static int ks8995_setup(struct dsa_switch *ds) ++{ ++ return 0; ++} ++ ++static int ks8995_port_enable(struct dsa_switch *ds, int port, ++ struct phy_device *phy) ++{ ++ struct ks8995_switch *ks = ds->priv; ++ ++ dev_dbg(ks->dev, "enable port %d\n", port); ++ ++ return 0; ++} ++ ++static void ks8995_port_disable(struct dsa_switch *ds, int port) ++{ ++ struct ks8995_switch *ks = ds->priv; ++ ++ dev_dbg(ks->dev, "disable port %d\n", port); ++} ++ ++static int ks8995_port_pre_bridge_flags(struct dsa_switch *ds, int port, ++ struct switchdev_brport_flags flags, ++ struct netlink_ext_ack *extack) ++{ ++ /* We support enabling/disabling learning */ ++ if (flags.mask & ~(BR_LEARNING)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int ks8995_port_bridge_flags(struct dsa_switch *ds, int port, ++ struct switchdev_brport_flags flags, ++ struct netlink_ext_ack *extack) ++{ ++ struct ks8995_switch *ks = ds->priv; ++ int ret; ++ u8 val; ++ ++ if (flags.mask & BR_LEARNING) { ++ ret = ks8995_read_reg(ks, KS8995_REG_PC(port, KS8995_REG_PC2), &val); ++ if (ret) { ++ dev_err(ks->dev, "failed to read KS8995_REG_PC2 on port %d\n", port); ++ return ret; ++ } ++ ++ if (flags.val & BR_LEARNING) ++ val &= ~KS8995_PC2_LEARN_DIS; ++ else ++ val |= KS8995_PC2_LEARN_DIS; ++ ++ ret = ks8995_write_reg(ks, KS8995_REG_PC(port, KS8995_REG_PC2), val); ++ if (ret) { ++ dev_err(ks->dev, "failed to write KS8995_REG_PC2 on port %d\n", port); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static void ks8995_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ++{ ++ struct ks8995_switch *ks = ds->priv; ++ int ret; ++ u8 val; ++ ++ ret = ks8995_read_reg(ks, KS8995_REG_PC(port, KS8995_REG_PC2), &val); ++ if (ret) { ++ dev_err(ks->dev, "failed to read KS8995_REG_PC2 on port %d\n", port); ++ return; ++ } ++ ++ /* Set the bits for the different STP states in accordance with ++ * the datasheet, pages 36-37 "Spanning tree support". ++ */ ++ switch (state) { ++ case BR_STATE_DISABLED: ++ case BR_STATE_BLOCKING: ++ case BR_STATE_LISTENING: ++ val &= ~KS8995_PC2_TXEN; ++ val &= ~KS8995_PC2_RXEN; ++ val |= KS8995_PC2_LEARN_DIS; ++ break; ++ case BR_STATE_LEARNING: ++ val &= ~KS8995_PC2_TXEN; ++ val &= ~KS8995_PC2_RXEN; ++ val &= ~KS8995_PC2_LEARN_DIS; ++ break; ++ case BR_STATE_FORWARDING: ++ val |= KS8995_PC2_TXEN; ++ val |= KS8995_PC2_RXEN; ++ val &= ~KS8995_PC2_LEARN_DIS; ++ break; ++ default: ++ dev_err(ks->dev, "unknown bridge state requested\n"); ++ return; ++ } ++ ++ ret = ks8995_write_reg(ks, KS8995_REG_PC(port, KS8995_REG_PC2), val); ++ if (ret) { ++ dev_err(ks->dev, "failed to write KS8995_REG_PC2 on port %d\n", port); ++ return; ++ } ++ ++ dev_dbg(ks->dev, "set KS8995_REG_PC2 for port %d to %02x\n", port, val); ++} ++ ++static void ks8995_phylink_get_caps(struct dsa_switch *dsa, int port, ++ struct phylink_config *config) ++{ ++ unsigned long *interfaces = config->supported_interfaces; ++ ++ if (port == KS8995_CPU_PORT) ++ __set_bit(PHY_INTERFACE_MODE_MII, interfaces); ++ ++ if (port <= 3) { ++ /* Internal PHYs */ ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, interfaces); ++ /* phylib default */ ++ __set_bit(PHY_INTERFACE_MODE_MII, interfaces); ++ } ++ ++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; ++} ++ ++/* Huge packet support up to 1916 byte packages "inclusive" ++ * which means that tags are included. If the bit is not set ++ * it is 1536 bytes "inclusive". We present the length without ++ * tags or ethernet headers. The setting affects all ports. ++ */ ++static int ks8995_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ++{ ++ struct ks8995_switch *ks = ds->priv; ++ unsigned int max_mtu; ++ int ret; ++ u8 val; ++ int i; ++ ++ ks->max_mtu[port] = new_mtu; ++ ++ /* Roof out the MTU for the entire switch to the greatest ++ * common denominator: the biggest set for any one port will ++ * be the biggest MTU for the switch. ++ */ ++ max_mtu = ETH_DATA_LEN; ++ for (i = 0; i < KS8995_NUM_PORTS; i++) { ++ if (ks->max_mtu[i] > max_mtu) ++ max_mtu = ks->max_mtu[i]; ++ } ++ ++ /* Translate to layer 2 size. ++ * Add ethernet and (possible) VLAN headers, and checksum to the size. ++ * For ETH_DATA_LEN (1500 bytes) this will add up to 1522 bytes. ++ */ ++ max_mtu += VLAN_ETH_HLEN; ++ max_mtu += ETH_FCS_LEN; ++ ++ ret = ks8995_read_reg(ks, KS8995_REG_GC2, &val); ++ if (ret) { ++ dev_err(ks->dev, "failed to read KS8995_REG_GC2\n"); ++ return ret; ++ } ++ ++ if (max_mtu <= 1522) { ++ val &= ~KS8995_GC2_HUGE; ++ val &= ~KS8995_GC2_LEGAL; ++ } else if (max_mtu > 1522 && max_mtu <= 1536) { ++ /* This accepts packets up to 1536 bytes */ ++ val &= ~KS8995_GC2_HUGE; ++ val |= KS8995_GC2_LEGAL; ++ } else { ++ /* This accepts packets up to 1916 bytes */ ++ val |= KS8995_GC2_HUGE; ++ val |= KS8995_GC2_LEGAL; ++ } ++ ++ dev_dbg(ks->dev, "new max MTU %d bytes (inclusive)\n", max_mtu); ++ ++ ret = ks8995_write_reg(ks, KS8995_REG_GC2, val); ++ if (ret) ++ dev_err(ks->dev, "failed to set KS8995_REG_GC2\n"); ++ ++ return ret; ++} ++ ++static int ks8995_get_max_mtu(struct dsa_switch *ds, int port) ++{ ++ return 1916 - ETH_HLEN - ETH_FCS_LEN; ++} ++ ++static const struct dsa_switch_ops ks8995_ds_ops = { ++ .get_tag_protocol = ks8995_get_tag_protocol, ++ .setup = ks8995_setup, ++ .port_pre_bridge_flags = ks8995_port_pre_bridge_flags, ++ .port_bridge_flags = ks8995_port_bridge_flags, ++ .port_enable = ks8995_port_enable, ++ .port_disable = ks8995_port_disable, ++ .port_stp_state_set = ks8995_port_stp_state_set, ++ .port_change_mtu = ks8995_change_mtu, ++ .port_max_mtu = ks8995_get_max_mtu, ++ .phylink_get_caps = ks8995_phylink_get_caps, ++}; ++ + /* ------------------------------------------------------------------------ */ + static int ks8995_probe(struct spi_device *spi) + { +@@ -388,6 +759,7 @@ static int ks8995_probe(struct spi_devic + + mutex_init(&ks->lock); + ks->spi = spi; ++ ks->dev = &spi->dev; + ks->chip = &ks8995_chip[variant]; + + ks->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", +@@ -434,6 +806,25 @@ static int ks8995_probe(struct spi_devic + dev_info(&spi->dev, "%s device found, Chip ID:%x, Revision:%x\n", + ks->chip->name, ks->chip->chip_id, ks->revision_id); + ++ err = ks8995_check_config(ks); ++ if (err) ++ return err; ++ ++ ks->ds = devm_kzalloc(&spi->dev, sizeof(*ks->ds), GFP_KERNEL); ++ if (!ks->ds) ++ return -ENOMEM; ++ ++ ks->ds->dev = &spi->dev; ++ ks->ds->num_ports = KS8995_NUM_PORTS; ++ ks->ds->ops = &ks8995_ds_ops; ++ ks->ds->phylink_mac_ops = &ks8995_phylink_mac_ops; ++ ks->ds->priv = ks; ++ ++ err = dsa_register_switch(ks->ds); ++ if (err) ++ return dev_err_probe(&spi->dev, err, ++ "unable to register DSA switch\n"); ++ + return 0; + } + +@@ -441,6 +832,7 @@ static void ks8995_remove(struct spi_dev + { + struct ks8995_switch *ks = spi_get_drvdata(spi); + ++ dsa_unregister_switch(ks->ds); + /* assert reset */ + gpiod_set_value_cansleep(ks->reset_gpio, 1); + } diff --git a/target/linux/generic/backport-6.12/784-01-v6.17-net-phy-realtek-add-error-handling-to-rtl8211f_get_w.patch b/target/linux/generic/backport-6.12/784-01-v6.17-net-phy-realtek-add-error-handling-to-rtl8211f_get_w.patch new file mode 100644 index 00000000000..5f3b119a89e --- /dev/null +++ b/target/linux/generic/backport-6.12/784-01-v6.17-net-phy-realtek-add-error-handling-to-rtl8211f_get_w.patch @@ -0,0 +1,39 @@ +From a9b24b3583ae1da7dbda031f141264f2da260219 Mon Sep 17 00:00:00 2001 +From: Daniel Braunwarth +Date: Tue, 24 Jun 2025 16:17:33 +0200 +Subject: [PATCH] net: phy: realtek: add error handling to rtl8211f_get_wol + +We should check if the WOL settings was successfully read from the PHY. + +In case this fails we cannot just use the error code and proceed. + +Signed-off-by: Daniel Braunwarth +Reported-by: Jon Hunter +Closes: https://lore.kernel.org/baaa083b-9a69-460f-ab35-2a7cb3246ffd@nvidia.com +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250624-realtek_fixes-v1-1-02a0b7c369bc@kuka.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -436,9 +436,15 @@ static irqreturn_t rtl8211f_handle_inter + + static void rtl8211f_get_wol(struct phy_device *dev, struct ethtool_wolinfo *wol) + { ++ int wol_events; ++ + wol->supported = WAKE_MAGIC; +- if (phy_read_paged(dev, RTL8211F_WOL_SETTINGS_PAGE, RTL8211F_WOL_SETTINGS_EVENTS) +- & RTL8211F_WOL_EVENT_MAGIC) ++ ++ wol_events = phy_read_paged(dev, RTL8211F_WOL_SETTINGS_PAGE, RTL8211F_WOL_SETTINGS_EVENTS); ++ if (wol_events < 0) ++ return; ++ ++ if (wol_events & RTL8211F_WOL_EVENT_MAGIC) + wol->wolopts = WAKE_MAGIC; + } + diff --git a/target/linux/generic/backport-6.12/784-02-v6.18-net-phy-realtek-Avoid-PHYCR2-access-if-PHYCR2-not-pr.patch b/target/linux/generic/backport-6.12/784-02-v6.18-net-phy-realtek-Avoid-PHYCR2-access-if-PHYCR2-not-pr.patch new file mode 100644 index 00000000000..681b234fc5a --- /dev/null +++ b/target/linux/generic/backport-6.12/784-02-v6.18-net-phy-realtek-Avoid-PHYCR2-access-if-PHYCR2-not-pr.patch @@ -0,0 +1,61 @@ +From 2c67301584f2671e320236df6bbe75ae09feb4d0 Mon Sep 17 00:00:00 2001 +From: Marek Vasut +Date: Sat, 11 Oct 2025 13:02:49 +0200 +Subject: [PATCH] net: phy: realtek: Avoid PHYCR2 access if PHYCR2 not present + +The driver is currently checking for PHYCR2 register presence in +rtl8211f_config_init(), but it does so after accessing PHYCR2 to +disable EEE. This was introduced in commit bfc17c165835 ("net: +phy: realtek: disable PHY-mode EEE"). Move the PHYCR2 presence +test before the EEE disablement and simplify the code. + +Fixes: bfc17c165835 ("net: phy: realtek: disable PHY-mode EEE") +Signed-off-by: Marek Vasut +Reviewed-by: Maxime Chevallier +Reviewed-by: Russell King (Oracle) +Link: https://patch.msgid.link/20251011110309.12664-1-marek.vasut@mailbox.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 23 +++++++++++------------ + 1 file changed, 11 insertions(+), 12 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -589,26 +589,25 @@ static int rtl8211f_config_init(struct p + str_enabled_disabled(val_rxdly)); + } + ++ if (!priv->has_phycr2) ++ return 0; ++ + /* Disable PHY-mode EEE so LPI is passed to the MAC */ + ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2, + RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0); + if (ret) + return ret; + +- if (priv->has_phycr2) { +- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, +- RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, +- priv->phycr2); +- if (ret < 0) { +- dev_err(dev, "clkout configuration failed: %pe\n", +- ERR_PTR(ret)); +- return ret; +- } +- +- return genphy_soft_reset(phydev); ++ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, ++ RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, ++ priv->phycr2); ++ if (ret < 0) { ++ dev_err(dev, "clkout configuration failed: %pe\n", ++ ERR_PTR(ret)); ++ return ret; + } + +- return 0; ++ return genphy_soft_reset(phydev); + } + + static int rtl821x_suspend(struct phy_device *phydev) diff --git a/target/linux/generic/backport-6.12/784-02-v6.18-net-phy-realtek-fix-RTL8211F-wake-on-lan-support.patch b/target/linux/generic/backport-6.12/784-02-v6.18-net-phy-realtek-fix-RTL8211F-wake-on-lan-support.patch new file mode 100644 index 00000000000..b5c4f8f9fce --- /dev/null +++ b/target/linux/generic/backport-6.12/784-02-v6.18-net-phy-realtek-fix-RTL8211F-wake-on-lan-support.patch @@ -0,0 +1,352 @@ +From b826bf795564ddef6402cf2cb522ae035bd117ae Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 13 Aug 2025 11:04:45 +0100 +Subject: [PATCH] net: phy: realtek: fix RTL8211F wake-on-lan support + +Implement Wake-on-Lan for RTL8211F correctly. The existing +implementation has multiple issues: + +1. It assumes that Wake-on-Lan can always be used, whether or not the + interrupt is wired, and whether or not the interrupt is capable of + waking the system. This breaks the ability for MAC drivers to detect + whether the PHY WoL is functional. +2. switching the interrupt pin in the .set_wol() method to PMEB mode + immediately silences link-state interrupts, which breaks phylib + when interrupts are being used rather than polling mode. +3. the code claiming to "reset WOL status" was doing nothing of the + sort. Bit 15 in page 0xd8a register 17 controls WoL reset, and + needs to be pulsed low to reset the WoL state. This bit was always + written as '1', resulting in no reset. +4. not resetting WoL state results in the PMEB pin remaining asserted, + which in turn leads to an interrupt storm. Only resetting the WoL + state in .set_wol() is not sufficient. +5. PMEB mode does not allow software detection of the wake-up event as + there is no status bit to indicate we received the WoL packet. +6. across reboots of at least the Jetson Xavier NX system, the WoL + configuration is preserved. + +Fix all of these issues by essentially rewriting the support. We: +1. clear the WoL event enable register at probe time. +2. detect whether we can support wake-up by having a valid interrupt, + and the "wakeup-source" property in DT. If we can, then we mark + the MDIO device as wakeup capable, and associate the interrupt + with the wakeup source. +3. arrange for the get_wol() and set_wol() implementations to handle + the case where the MDIO device has not been marked as wakeup + capable (thereby returning no WoL support, and refusing to enable + WoL support.) +4. avoid switching to PMEB mode, instead using INTB mode with the + interrupt enable, reconfiguring the interrupt enables at suspend + time, and restoring their original state at resume time (we track + the state of the interrupt enable register in .config_intr() + register.) +5. move WoL reset from .set_wol() to the suspend function to ensure + that WoL state is cleared prior to suspend. This is necessary + after the PME interrupt has been enabled as a second WoL packet + will not re-raise a previously cleared PME interrupt. +6. when a PME interrupt (for wakeup) is asserted, pass this to the + PM wakeup so it knows which device woke the system. + +This fixes WoL support in the Realtek RTL8211F driver when used on the +nVidia Jetson Xavier NX platform, and needs to be applied before stmmac +patches which allow these platforms to forward the ethtool WoL commands +to the Realtek PHY. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/E1um8Ld-008jxD-Mc@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 172 ++++++++++++++++++++----- + 1 file changed, 140 insertions(+), 32 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -31,6 +32,7 @@ + #define RTL821x_INER 0x12 + #define RTL8211B_INER_INIT 0x6400 + #define RTL8211E_INER_LINK_STATUS BIT(10) ++#define RTL8211F_INER_PME BIT(7) + #define RTL8211F_INER_LINK_STATUS BIT(4) + + #define RTL821x_INSR 0x13 +@@ -96,17 +98,13 @@ + #define RTL8211F_RXCR 0x15 + #define RTL8211F_RX_DELAY BIT(3) + +-/* RTL8211F WOL interrupt configuration */ +-#define RTL8211F_INTBCR_PAGE 0xd40 +-#define RTL8211F_INTBCR 0x16 +-#define RTL8211F_INTBCR_INTB_PMEB BIT(5) +- + /* RTL8211F WOL settings */ +-#define RTL8211F_WOL_SETTINGS_PAGE 0xd8a ++#define RTL8211F_WOL_PAGE 0xd8a + #define RTL8211F_WOL_SETTINGS_EVENTS 16 + #define RTL8211F_WOL_EVENT_MAGIC BIT(12) +-#define RTL8211F_WOL_SETTINGS_STATUS 17 +-#define RTL8211F_WOL_STATUS_RESET (BIT(15) | 0x1fff) ++#define RTL8211F_WOL_RST_RMSQ 17 ++#define RTL8211F_WOL_RG_RSTB BIT(15) ++#define RTL8211F_WOL_RMSQ 0x1fff + + /* RTL8211F Unique phyiscal and multicast address (WOL) */ + #define RTL8211F_PHYSICAL_ADDR_PAGE 0xd8c +@@ -172,7 +170,8 @@ struct rtl821x_priv { + u16 phycr2; + bool has_phycr2; + struct clk *clk; +- u32 saved_wolopts; ++ /* rtl8211f */ ++ u16 iner; + }; + + static int rtl821x_read_page(struct phy_device *phydev) +@@ -255,6 +254,34 @@ static int rtl821x_probe(struct phy_devi + return 0; + } + ++static int rtl8211f_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ int ret; ++ ++ ret = rtl821x_probe(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* Disable all PME events */ ++ ret = phy_write_paged(phydev, RTL8211F_WOL_PAGE, ++ RTL8211F_WOL_SETTINGS_EVENTS, 0); ++ if (ret < 0) ++ return ret; ++ ++ /* Mark this PHY as wakeup capable and register the interrupt as a ++ * wakeup IRQ if the PHY is marked as a wakeup source in firmware, ++ * and the interrupt is valid. ++ */ ++ if (device_property_read_bool(dev, "wakeup-source") && ++ phy_interrupt_is_valid(phydev)) { ++ device_set_wakeup_capable(dev, true); ++ devm_pm_set_wake_irq(dev, phydev->irq); ++ } ++ ++ return ret; ++} ++ + static int rtl8201_ack_interrupt(struct phy_device *phydev) + { + int err; +@@ -352,6 +379,7 @@ static int rtl8211e_config_intr(struct p + + static int rtl8211f_config_intr(struct phy_device *phydev) + { ++ struct rtl821x_priv *priv = phydev->priv; + u16 val; + int err; + +@@ -362,8 +390,10 @@ static int rtl8211f_config_intr(struct p + + val = RTL8211F_INER_LINK_STATUS; + err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val); ++ if (err == 0) ++ priv->iner = val; + } else { +- val = 0; ++ priv->iner = val = 0; + err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val); + if (err) + return err; +@@ -426,21 +456,34 @@ static irqreturn_t rtl8211f_handle_inter + return IRQ_NONE; + } + +- if (!(irq_status & RTL8211F_INER_LINK_STATUS)) +- return IRQ_NONE; ++ if (irq_status & RTL8211F_INER_LINK_STATUS) { ++ phy_trigger_machine(phydev); ++ return IRQ_HANDLED; ++ } + +- phy_trigger_machine(phydev); ++ if (irq_status & RTL8211F_INER_PME) { ++ pm_wakeup_event(&phydev->mdio.dev, 0); ++ return IRQ_HANDLED; ++ } + +- return IRQ_HANDLED; ++ return IRQ_NONE; + } + + static void rtl8211f_get_wol(struct phy_device *dev, struct ethtool_wolinfo *wol) + { + int wol_events; + ++ /* If the PHY is not capable of waking the system, then WoL can not ++ * be supported. ++ */ ++ if (!device_can_wakeup(&dev->mdio.dev)) { ++ wol->supported = 0; ++ return; ++ } ++ + wol->supported = WAKE_MAGIC; + +- wol_events = phy_read_paged(dev, RTL8211F_WOL_SETTINGS_PAGE, RTL8211F_WOL_SETTINGS_EVENTS); ++ wol_events = phy_read_paged(dev, RTL8211F_WOL_PAGE, RTL8211F_WOL_SETTINGS_EVENTS); + if (wol_events < 0) + return; + +@@ -453,6 +496,9 @@ static int rtl8211f_set_wol(struct phy_d + const u8 *mac_addr = dev->attached_dev->dev_addr; + int oldpage; + ++ if (!device_can_wakeup(&dev->mdio.dev)) ++ return -EOPNOTSUPP; ++ + oldpage = phy_save_page(dev); + if (oldpage < 0) + goto err; +@@ -464,25 +510,23 @@ static int rtl8211f_set_wol(struct phy_d + __phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD1, mac_addr[3] << 8 | (mac_addr[2])); + __phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD2, mac_addr[5] << 8 | (mac_addr[4])); + +- /* Enable magic packet matching and reset WOL status */ +- rtl821x_write_page(dev, RTL8211F_WOL_SETTINGS_PAGE); ++ /* Enable magic packet matching */ ++ rtl821x_write_page(dev, RTL8211F_WOL_PAGE); + __phy_write(dev, RTL8211F_WOL_SETTINGS_EVENTS, RTL8211F_WOL_EVENT_MAGIC); +- __phy_write(dev, RTL8211F_WOL_SETTINGS_STATUS, RTL8211F_WOL_STATUS_RESET); +- +- /* Enable the WOL interrupt */ +- rtl821x_write_page(dev, RTL8211F_INTBCR_PAGE); +- __phy_set_bits(dev, RTL8211F_INTBCR, RTL8211F_INTBCR_INTB_PMEB); ++ /* Set the maximum packet size, and assert WoL reset */ ++ __phy_write(dev, RTL8211F_WOL_RST_RMSQ, RTL8211F_WOL_RMSQ); + } else { +- /* Disable the WOL interrupt */ +- rtl821x_write_page(dev, RTL8211F_INTBCR_PAGE); +- __phy_clear_bits(dev, RTL8211F_INTBCR, RTL8211F_INTBCR_INTB_PMEB); +- +- /* Disable magic packet matching and reset WOL status */ +- rtl821x_write_page(dev, RTL8211F_WOL_SETTINGS_PAGE); ++ /* Disable magic packet matching */ ++ rtl821x_write_page(dev, RTL8211F_WOL_PAGE); + __phy_write(dev, RTL8211F_WOL_SETTINGS_EVENTS, 0); +- __phy_write(dev, RTL8211F_WOL_SETTINGS_STATUS, RTL8211F_WOL_STATUS_RESET); ++ ++ /* Place WoL in reset */ ++ __phy_clear_bits(dev, RTL8211F_WOL_RST_RMSQ, ++ RTL8211F_WOL_RG_RSTB); + } + ++ device_set_wakeup_enable(&dev->mdio.dev, !!(wol->wolopts & WAKE_MAGIC)); ++ + err: + return phy_restore_page(dev, oldpage, 0); + } +@@ -627,6 +671,52 @@ static int rtl821x_suspend(struct phy_de + return ret; + } + ++static int rtl8211f_suspend(struct phy_device *phydev) ++{ ++ u16 wol_rst; ++ int ret; ++ ++ ret = rtl821x_suspend(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* If a PME event is enabled, then configure the interrupt for ++ * PME events only, disabling link interrupt. We avoid switching ++ * to PMEB mode as we don't have a status bit for that. ++ */ ++ if (device_may_wakeup(&phydev->mdio.dev)) { ++ ret = phy_write_paged(phydev, 0xa42, RTL821x_INER, ++ RTL8211F_INER_PME); ++ if (ret < 0) ++ goto err; ++ ++ /* Read the INSR to clear any pending interrupt */ ++ phy_read_paged(phydev, RTL8211F_INSR_PAGE, RTL8211F_INSR); ++ ++ /* Reset the WoL to ensure that an event is picked up. ++ * Unless we do this, even if we receive another packet, ++ * we may not have a PME interrupt raised. ++ */ ++ ret = phy_read_paged(phydev, RTL8211F_WOL_PAGE, ++ RTL8211F_WOL_RST_RMSQ); ++ if (ret < 0) ++ goto err; ++ ++ wol_rst = ret & ~RTL8211F_WOL_RG_RSTB; ++ ret = phy_write_paged(phydev, RTL8211F_WOL_PAGE, ++ RTL8211F_WOL_RST_RMSQ, wol_rst); ++ if (ret < 0) ++ goto err; ++ ++ wol_rst |= RTL8211F_WOL_RG_RSTB; ++ ret = phy_write_paged(phydev, RTL8211F_WOL_PAGE, ++ RTL8211F_WOL_RST_RMSQ, wol_rst); ++ } ++ ++err: ++ return ret; ++} ++ + static int rtl821x_resume(struct phy_device *phydev) + { + struct rtl821x_priv *priv = phydev->priv; +@@ -644,6 +734,24 @@ static int rtl821x_resume(struct phy_dev + return 0; + } + ++static int rtl8211f_resume(struct phy_device *phydev) ++{ ++ struct rtl821x_priv *priv = phydev->priv; ++ int ret; ++ ++ ret = rtl821x_resume(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* If the device was programmed for a PME event, restore the interrupt ++ * enable so phylib can receive link state interrupts. ++ */ ++ if (device_may_wakeup(&phydev->mdio.dev)) ++ ret = phy_write_paged(phydev, 0xa42, RTL821x_INER, priv->iner); ++ ++ return ret; ++} ++ + static int rtl8211x_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) + { +@@ -1639,15 +1747,15 @@ static struct phy_driver realtek_drvs[] + }, { + PHY_ID_MATCH_EXACT(0x001cc916), + .name = "RTL8211F Gigabit Ethernet", +- .probe = rtl821x_probe, ++ .probe = rtl8211f_probe, + .config_init = &rtl8211f_config_init, + .read_status = rtlgen_read_status, + .config_intr = &rtl8211f_config_intr, + .handle_interrupt = rtl8211f_handle_interrupt, + .set_wol = rtl8211f_set_wol, + .get_wol = rtl8211f_get_wol, +- .suspend = rtl821x_suspend, +- .resume = rtl821x_resume, ++ .suspend = rtl8211f_suspend, ++ .resume = rtl8211f_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + .flags = PHY_ALWAYS_CALL_SUSPEND, diff --git a/target/linux/generic/backport-6.12/784-03-v6.18-net-phy-realtek-fix-rtl8221b-vm-cg-name.patch b/target/linux/generic/backport-6.12/784-03-v6.18-net-phy-realtek-fix-rtl8221b-vm-cg-name.patch new file mode 100644 index 00000000000..ab319f08066 --- /dev/null +++ b/target/linux/generic/backport-6.12/784-03-v6.18-net-phy-realtek-fix-rtl8221b-vm-cg-name.patch @@ -0,0 +1,70 @@ +From ffff5c8fc2af2218a3332b3d5b97654599d50cde Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Thu, 16 Oct 2025 21:22:52 +0200 +Subject: [PATCH] net: phy: realtek: fix rtl8221b-vm-cg name + +When splitting the RTL8221B-VM-CG into C22 and C45 variants, the name was +accidentally changed to RTL8221B-VN-CG. This patch brings back the previous +part number. + +Fixes: ad5ce743a6b0 ("net: phy: realtek: Add driver instances for rtl8221b via Clause 45") +Signed-off-by: Aleksander Jan Bajkowski +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20251016192325.2306757-1-olek2@wp.pl +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -154,7 +154,7 @@ + #define RTL_8211FVD_PHYID 0x001cc878 + #define RTL_8221B 0x001cc840 + #define RTL_8221B_VB_CG 0x001cc849 +-#define RTL_8221B_VN_CG 0x001cc84a ++#define RTL_8221B_VM_CG 0x001cc84a + #define RTL_8251B 0x001cc862 + #define RTL_8261C 0x001cc890 + +@@ -1498,16 +1498,16 @@ static int rtl8221b_vb_cg_c45_match_phy_ + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true); + } + +-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev, ++static int rtl8221b_vm_cg_c22_match_phy_device(struct phy_device *phydev, + const struct phy_driver *phydrv) + { +- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false); ++ return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, false); + } + +-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev, ++static int rtl8221b_vm_cg_c45_match_phy_device(struct phy_device *phydev, + const struct phy_driver *phydrv) + { +- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true); ++ return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, true); + } + + static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev, +@@ -1854,7 +1854,7 @@ static struct phy_driver realtek_drvs[] + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, + }, { +- .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device, ++ .match_phy_device = rtl8221b_vm_cg_c22_match_phy_device, + .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", + .probe = rtl822x_probe, + .get_features = rtl822x_get_features, +@@ -1867,8 +1867,8 @@ static struct phy_driver realtek_drvs[] + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + }, { +- .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device, +- .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)", ++ .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device, ++ .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)", + .probe = rtl822x_probe, + .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, diff --git a/target/linux/generic/backport-6.12/784-03-v6.18-net-phy-realtek-support-for-TRIGGER_NETDEV_LINK-on-R.patch b/target/linux/generic/backport-6.12/784-03-v6.18-net-phy-realtek-support-for-TRIGGER_NETDEV_LINK-on-R.patch new file mode 100644 index 00000000000..dc8d247daec --- /dev/null +++ b/target/linux/generic/backport-6.12/784-03-v6.18-net-phy-realtek-support-for-TRIGGER_NETDEV_LINK-on-R.patch @@ -0,0 +1,105 @@ +From f63f21e82ecafd288b100ea161247820bf1e92c4 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Mon, 25 Aug 2025 23:09:49 +0200 +Subject: [PATCH] net: phy: realtek: support for TRIGGER_NETDEV_LINK on + RTL8211E and RTL8211F + +This patch adds support for the TRIGGER_NETDEV_LINK trigger. It activates +the LED when a link is established, regardless of the speed. + +Tested on Orange Pi PC2 with RTL8211E PHY. + +Signed-off-by: Aleksander Jan Bajkowski +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250825211059.143231-1-olek2@wp.pl +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 39 +++++++++++++++++++++----- + 1 file changed, 32 insertions(+), 7 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -755,7 +755,8 @@ static int rtl8211f_resume(struct phy_de + static int rtl8211x_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) + { +- const unsigned long mask = BIT(TRIGGER_NETDEV_LINK_10) | ++ const unsigned long mask = BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | + BIT(TRIGGER_NETDEV_LINK_100) | + BIT(TRIGGER_NETDEV_LINK_1000) | + BIT(TRIGGER_NETDEV_RX) | +@@ -813,6 +814,12 @@ static int rtl8211f_led_hw_control_get(s + if (val & RTL8211F_LEDCR_LINK_1000) + __set_bit(TRIGGER_NETDEV_LINK_1000, rules); + ++ if ((val & RTL8211F_LEDCR_LINK_10) && ++ (val & RTL8211F_LEDCR_LINK_100) && ++ (val & RTL8211F_LEDCR_LINK_1000)) { ++ __set_bit(TRIGGER_NETDEV_LINK, rules); ++ } ++ + if (val & RTL8211F_LEDCR_ACT_TXRX) { + __set_bit(TRIGGER_NETDEV_RX, rules); + __set_bit(TRIGGER_NETDEV_TX, rules); +@@ -830,14 +837,20 @@ static int rtl8211f_led_hw_control_set(s + if (index >= RTL8211x_LED_COUNT) + return -EINVAL; + +- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) || ++ test_bit(TRIGGER_NETDEV_LINK_10, &rules)) { + reg |= RTL8211F_LEDCR_LINK_10; ++ } + +- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) || ++ test_bit(TRIGGER_NETDEV_LINK_100, &rules)) { + reg |= RTL8211F_LEDCR_LINK_100; ++ } + +- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) || ++ test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) { + reg |= RTL8211F_LEDCR_LINK_1000; ++ } + + if (test_bit(TRIGGER_NETDEV_RX, &rules) || + test_bit(TRIGGER_NETDEV_TX, &rules)) { +@@ -885,6 +898,12 @@ static int rtl8211e_led_hw_control_get(s + if (cr2 & RTL8211E_LEDCR2_LINK_1000) + __set_bit(TRIGGER_NETDEV_LINK_1000, rules); + ++ if ((cr2 & RTL8211E_LEDCR2_LINK_10) && ++ (cr2 & RTL8211E_LEDCR2_LINK_100) && ++ (cr2 & RTL8211E_LEDCR2_LINK_1000)) { ++ __set_bit(TRIGGER_NETDEV_LINK, rules); ++ } ++ + return ret; + } + +@@ -912,14 +931,20 @@ static int rtl8211e_led_hw_control_set(s + if (ret < 0) + return ret; + +- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) || ++ test_bit(TRIGGER_NETDEV_LINK_10, &rules)) { + cr2 |= RTL8211E_LEDCR2_LINK_10; ++ } + +- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) || ++ test_bit(TRIGGER_NETDEV_LINK_100, &rules)) { + cr2 |= RTL8211E_LEDCR2_LINK_100; ++ } + +- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) || ++ test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) { + cr2 |= RTL8211E_LEDCR2_LINK_1000; ++ } + + cr2 <<= RTL8211E_LEDCR2_SHIFT * index; + ret = rtl821x_modify_ext_page(phydev, RTL8211E_LEDCR_EXT_PAGE, diff --git a/target/linux/generic/backport-6.12/784-06-v6.19-net-phy-realtek-Add-RTL8224-cable-testing-support.patch b/target/linux/generic/backport-6.12/784-06-v6.19-net-phy-realtek-Add-RTL8224-cable-testing-support.patch new file mode 100644 index 00000000000..d95211dc659 --- /dev/null +++ b/target/linux/generic/backport-6.12/784-06-v6.19-net-phy-realtek-Add-RTL8224-cable-testing-support.patch @@ -0,0 +1,272 @@ +From 61958b33ef0bab1c1874c933cd3910f495526782 Mon Sep 17 00:00:00 2001 +From: Issam Hamdi +Date: Fri, 24 Oct 2025 11:49:00 +0200 +Subject: [PATCH] net: phy: realtek: Add RTL8224 cable testing support + +The RTL8224 can detect open pairs and short types (in same pair or some +other pair). The distance to this problem can be estimated. This is done +for each of the 4 pairs separately. + +It is not meant to be run while there is an active link partner because +this interferes with the active test pulses. + +Output with open 50 m cable: + + Pair A code Open Circuit, source: TDR + Pair A, fault length: 51.79m, source: TDR + Pair B code Open Circuit, source: TDR + Pair B, fault length: 51.28m, source: TDR + Pair C code Open Circuit, source: TDR + Pair C, fault length: 50.46m, source: TDR + Pair D code Open Circuit, source: TDR + Pair D, fault length: 51.12m, source: TDR + +Terminated cable: + + Pair A code OK, source: TDR + Pair B code OK, source: TDR + Pair C code OK, source: TDR + Pair D code OK, source: TDR + +Shorted cable (both short types are at roughly the same distance) + + Pair A code Short to another pair, source: TDR + Pair A, fault length: 2.35m, source: TDR + Pair B code Short to another pair, source: TDR + Pair B, fault length: 2.15m, source: TDR + Pair C code OK, source: TDR + Pair D code Short within Pair, source: TDR + Pair D, fault length: 1.94m, source: TDR + +Signed-off-by: Issam Hamdi +Co-developed-by: Sven Eckelmann +Signed-off-by: Sven Eckelmann +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20251024-rtl8224-cable-test-v1-1-e3cda89ac98f@simonwunderlich.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 187 +++++++++++++++++++++++++ + 1 file changed, 187 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -8,6 +8,7 @@ + * Copyright (c) 2004 Freescale Semiconductor, Inc. + */ + #include ++#include + #include + #include + #include +@@ -127,6 +128,27 @@ + */ + #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg)) + ++#define RTL8224_MII_RTCT 0x11 ++#define RTL8224_MII_RTCT_ENABLE BIT(0) ++#define RTL8224_MII_RTCT_PAIR_A BIT(4) ++#define RTL8224_MII_RTCT_PAIR_B BIT(5) ++#define RTL8224_MII_RTCT_PAIR_C BIT(6) ++#define RTL8224_MII_RTCT_PAIR_D BIT(7) ++#define RTL8224_MII_RTCT_DONE BIT(15) ++ ++#define RTL8224_MII_SRAM_ADDR 0x1b ++#define RTL8224_MII_SRAM_DATA 0x1c ++ ++#define RTL8224_SRAM_RTCT_FAULT(pair) (0x8026 + (pair) * 4) ++#define RTL8224_SRAM_RTCT_FAULT_BUSY BIT(0) ++#define RTL8224_SRAM_RTCT_FAULT_OPEN BIT(3) ++#define RTL8224_SRAM_RTCT_FAULT_SAME_SHORT BIT(4) ++#define RTL8224_SRAM_RTCT_FAULT_OK BIT(5) ++#define RTL8224_SRAM_RTCT_FAULT_DONE BIT(6) ++#define RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT BIT(7) ++ ++#define RTL8224_SRAM_RTCT_LEN(pair) (0x8028 + (pair) * 4) ++ + #define RTL8366RB_POWER_SAVE 0x15 + #define RTL8366RB_POWER_SAVE_ON BIT(12) + +@@ -1453,6 +1475,168 @@ static int rtl822xb_c45_read_status(stru + return 0; + } + ++static int rtl8224_cable_test_start(struct phy_device *phydev) ++{ ++ u32 val; ++ int ret; ++ ++ /* disable auto-negotiation and force 1000/Full */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, ++ RTL822X_VND2_C22_REG(MII_BMCR), ++ BMCR_ANENABLE | BMCR_SPEED100 | BMCR_SPEED10, ++ BMCR_SPEED1000 | BMCR_FULLDPLX); ++ if (ret) ++ return ret; ++ ++ mdelay(500); ++ ++ /* trigger cable test */ ++ val = RTL8224_MII_RTCT_ENABLE; ++ val |= RTL8224_MII_RTCT_PAIR_A; ++ val |= RTL8224_MII_RTCT_PAIR_B; ++ val |= RTL8224_MII_RTCT_PAIR_C; ++ val |= RTL8224_MII_RTCT_PAIR_D; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, ++ RTL822X_VND2_C22_REG(RTL8224_MII_RTCT), ++ RTL8224_MII_RTCT_DONE, val); ++} ++ ++static int rtl8224_sram_read(struct phy_device *phydev, u32 reg) ++{ ++ int ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, ++ RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_ADDR), ++ reg); ++ if (ret) ++ return ret; ++ ++ return phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_DATA)); ++} ++ ++static int rtl8224_pair_len_get(struct phy_device *phydev, u32 pair) ++{ ++ int cable_len; ++ u32 reg_len; ++ int ret; ++ u32 cm; ++ ++ reg_len = RTL8224_SRAM_RTCT_LEN(pair); ++ ++ ret = rtl8224_sram_read(phydev, reg_len); ++ if (ret < 0) ++ return ret; ++ ++ cable_len = ret & 0xff00; ++ ++ ret = rtl8224_sram_read(phydev, reg_len + 1); ++ if (ret < 0) ++ return ret; ++ ++ cable_len |= (ret & 0xff00) >> 8; ++ ++ cable_len -= 620; ++ cable_len = max(cable_len, 0); ++ ++ cm = cable_len * 100 / 78; ++ ++ return cm; ++} ++ ++static int rtl8224_cable_test_result_trans(u32 result) ++{ ++ if (!(result & RTL8224_SRAM_RTCT_FAULT_DONE)) ++ return -EBUSY; ++ ++ if (result & RTL8224_SRAM_RTCT_FAULT_OK) ++ return ETHTOOL_A_CABLE_RESULT_CODE_OK; ++ ++ if (result & RTL8224_SRAM_RTCT_FAULT_OPEN) ++ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ ++ if (result & RTL8224_SRAM_RTCT_FAULT_SAME_SHORT) ++ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; ++ ++ if (result & RTL8224_SRAM_RTCT_FAULT_BUSY) ++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; ++ ++ if (result & RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT) ++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; ++ ++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; ++} ++ ++static int rtl8224_cable_test_report_pair(struct phy_device *phydev, unsigned int pair) ++{ ++ int fault_rslt; ++ int ret; ++ ++ ret = rtl8224_sram_read(phydev, RTL8224_SRAM_RTCT_FAULT(pair)); ++ if (ret < 0) ++ return ret; ++ ++ fault_rslt = rtl8224_cable_test_result_trans(ret); ++ if (fault_rslt < 0) ++ return 0; ++ ++ ret = ethnl_cable_test_result(phydev, pair, fault_rslt); ++ if (ret < 0) ++ return ret; ++ ++ switch (fault_rslt) { ++ case ETHTOOL_A_CABLE_RESULT_CODE_OPEN: ++ case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT: ++ case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT: ++ ret = rtl8224_pair_len_get(phydev, pair); ++ if (ret < 0) ++ return ret; ++ ++ return ethnl_cable_test_fault_length(phydev, pair, ret); ++ default: ++ return 0; ++ } ++} ++ ++static int rtl8224_cable_test_report(struct phy_device *phydev, bool *finished) ++{ ++ unsigned int pair; ++ int ret; ++ ++ for (pair = ETHTOOL_A_CABLE_PAIR_A; pair <= ETHTOOL_A_CABLE_PAIR_D; pair++) { ++ ret = rtl8224_cable_test_report_pair(phydev, pair); ++ if (ret == -EBUSY) { ++ *finished = false; ++ return 0; ++ } ++ ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int rtl8224_cable_test_get_status(struct phy_device *phydev, bool *finished) ++{ ++ int ret; ++ ++ *finished = false; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ RTL822X_VND2_C22_REG(RTL8224_MII_RTCT)); ++ if (ret < 0) ++ return ret; ++ ++ if (!(ret & RTL8224_MII_RTCT_DONE)) ++ return 0; ++ ++ *finished = true; ++ ++ return rtl8224_cable_test_report(phydev, finished); ++} ++ + static bool rtlgen_supports_2_5gbps(struct phy_device *phydev) + { + int val; +@@ -1930,11 +2114,14 @@ static struct phy_driver realtek_drvs[] + }, { + PHY_ID_MATCH_EXACT(0x001ccad0), + .name = "RTL8224 2.5Gbps PHY", ++ .flags = PHY_POLL_CABLE_TEST, + .get_features = rtl822x_c45_get_features, + .config_aneg = rtl822x_c45_config_aneg, + .read_status = rtl822x_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, ++ .cable_test_start = rtl8224_cable_test_start, ++ .cable_test_get_status = rtl8224_cable_test_get_status, + }, { + PHY_ID_MATCH_EXACT(0x001cc961), + .name = "RTL8366RB Gigabit Ethernet", diff --git a/target/linux/generic/backport-6.12/784-07-v6.19-net-phy-realtek-add-interrupt-support-for-RTL8221B.patch b/target/linux/generic/backport-6.12/784-07-v6.19-net-phy-realtek-add-interrupt-support-for-RTL8221B.patch new file mode 100644 index 00000000000..2efa4e961bf --- /dev/null +++ b/target/linux/generic/backport-6.12/784-07-v6.19-net-phy-realtek-add-interrupt-support-for-RTL8221B.patch @@ -0,0 +1,105 @@ +From 18aa36238a4d835c1644dcccd63d32c7fdd4b310 Mon Sep 17 00:00:00 2001 +From: Jianhui Zhao +Date: Sun, 2 Nov 2025 16:26:37 +0100 +Subject: [PATCH] net: phy: realtek: add interrupt support for RTL8221B + +This commit introduces interrupt support for RTL8221B (C45 mode). +Interrupts are mapped on the VEND2 page. VEND2 registers are only +accessible via C45 reads and cannot be accessed by C45 over C22. + +Signed-off-by: Jianhui Zhao +[Enable only link state change interrupts] +Signed-off-by: Aleksander Jan Bajkowski +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20251102152644.1676482-1-olek2@wp.pl +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 56 ++++++++++++++++++++++++++ + 1 file changed, 56 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -128,6 +128,11 @@ + */ + #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg)) + ++#define RTL8221B_VND2_INER 0xa4d2 ++#define RTL8221B_VND2_INER_LINK_STATUS BIT(4) ++ ++#define RTL8221B_VND2_INSR 0xa4d4 ++ + #define RTL8224_MII_RTCT 0x11 + #define RTL8224_MII_RTCT_ENABLE BIT(0) + #define RTL8224_MII_RTCT_PAIR_A BIT(4) +@@ -1880,6 +1885,53 @@ static irqreturn_t rtl9000a_handle_inter + return IRQ_HANDLED; + } + ++static int rtl8221b_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR); ++ ++ return (err < 0) ? err : 0; ++} ++ ++static int rtl8221b_config_intr(struct phy_device *phydev) ++{ ++ int err; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ err = rtl8221b_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ err = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER, ++ RTL8221B_VND2_INER_LINK_STATUS); ++ } else { ++ err = phy_write_mmd(phydev, MDIO_MMD_VEND2, ++ RTL8221B_VND2_INER, 0); ++ if (err) ++ return err; ++ ++ err = rtl8221b_ack_interrupt(phydev); ++ } ++ ++ return err; ++} ++ ++static irqreturn_t rtl8221b_handle_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = rtl8221b_ack_interrupt(phydev); ++ if (err) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ + static struct phy_driver realtek_drvs[] = { + { + PHY_ID_MATCH_EXACT(0x00008201), +@@ -2054,6 +2106,8 @@ static struct phy_driver realtek_drvs[] + }, { + .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, + .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", ++ .config_intr = rtl8221b_config_intr, ++ .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, + .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, +@@ -2078,6 +2132,8 @@ static struct phy_driver realtek_drvs[] + }, { + .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device, + .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)", ++ .config_intr = rtl8221b_config_intr, ++ .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, + .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, diff --git a/target/linux/generic/backport-6.12/784-08-v6.19-net-phy-realtek-create-rtl8211f_config_rgmii_delay.patch b/target/linux/generic/backport-6.12/784-08-v6.19-net-phy-realtek-create-rtl8211f_config_rgmii_delay.patch new file mode 100644 index 00000000000..0c523ee6834 --- /dev/null +++ b/target/linux/generic/backport-6.12/784-08-v6.19-net-phy-realtek-create-rtl8211f_config_rgmii_delay.patch @@ -0,0 +1,128 @@ +From 8e982441ba601d982dd0739972115d85ae01d99b Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 18 Nov 2025 01:40:28 +0200 +Subject: [PATCH] net: phy: realtek: create rtl8211f_config_rgmii_delay() + +The control flow in rtl8211f_config_init() has some pitfalls which were +probably unintended. Specifically it has an early return: + + switch (phydev->interface) { + ... + default: /* the rest of the modes imply leaving delay as is. */ + return 0; + } + +which exits the entire config_init() function. This means it also skips +doing things such as disabling CLKOUT or disabling PHY-mode EEE. + +For the RTL8211FS, which uses PHY_INTERFACE_MODE_SGMII, this might be a +problem. However, I don't know that it is, so there is no Fixes: tag. +The issue was observed through code inspection. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20251117234033.345679-2-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 65 +++++++++++++++----------- + 1 file changed, 39 insertions(+), 26 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -587,22 +587,11 @@ static int rtl8211c_config_init(struct p + CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER); + } + +-static int rtl8211f_config_init(struct phy_device *phydev) ++static int rtl8211f_config_rgmii_delay(struct phy_device *phydev) + { +- struct rtl821x_priv *priv = phydev->priv; +- struct device *dev = &phydev->mdio.dev; + u16 val_txdly, val_rxdly; + int ret; + +- ret = phy_modify_paged_changed(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1, +- RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF, +- priv->phycr1); +- if (ret < 0) { +- dev_err(dev, "aldps mode configuration failed: %pe\n", +- ERR_PTR(ret)); +- return ret; +- } +- + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + val_txdly = 0; +@@ -632,34 +621,58 @@ static int rtl8211f_config_init(struct p + RTL8211F_TXCR, RTL8211F_TX_DELAY, + val_txdly); + if (ret < 0) { +- dev_err(dev, "Failed to update the TX delay register\n"); ++ phydev_err(phydev, "Failed to update the TX delay register: %pe\n", ++ ERR_PTR(ret)); + return ret; + } else if (ret) { +- dev_dbg(dev, +- "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n", +- str_enable_disable(val_txdly)); ++ phydev_dbg(phydev, ++ "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n", ++ str_enable_disable(val_txdly)); + } else { +- dev_dbg(dev, +- "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n", +- str_enabled_disabled(val_txdly)); ++ phydev_dbg(phydev, ++ "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n", ++ str_enabled_disabled(val_txdly)); + } + + ret = phy_modify_paged_changed(phydev, RTL8211F_RGMII_PAGE, + RTL8211F_RXCR, RTL8211F_RX_DELAY, + val_rxdly); + if (ret < 0) { +- dev_err(dev, "Failed to update the RX delay register\n"); ++ phydev_err(phydev, "Failed to update the RX delay register: %pe\n", ++ ERR_PTR(ret)); + return ret; + } else if (ret) { +- dev_dbg(dev, +- "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n", +- str_enable_disable(val_rxdly)); ++ phydev_dbg(phydev, ++ "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n", ++ str_enable_disable(val_rxdly)); + } else { +- dev_dbg(dev, +- "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n", +- str_enabled_disabled(val_rxdly)); ++ phydev_dbg(phydev, ++ "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n", ++ str_enabled_disabled(val_rxdly)); + } + ++ return 0; ++} ++ ++static int rtl8211f_config_init(struct phy_device *phydev) ++{ ++ struct rtl821x_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ int ret; ++ ++ ret = phy_modify_paged_changed(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1, ++ RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF, ++ priv->phycr1); ++ if (ret < 0) { ++ dev_err(dev, "aldps mode configuration failed: %pe\n", ++ ERR_PTR(ret)); ++ return ret; ++ } ++ ++ ret = rtl8211f_config_rgmii_delay(phydev); ++ if (ret) ++ return ret; ++ + if (!priv->has_phycr2) + return 0; + diff --git a/target/linux/generic/backport-6.12/784-09-v6.19-net-phy-realtek-eliminate-priv-phycr2-variable.patch b/target/linux/generic/backport-6.12/784-09-v6.19-net-phy-realtek-eliminate-priv-phycr2-variable.patch new file mode 100644 index 00000000000..1c67961d2ef --- /dev/null +++ b/target/linux/generic/backport-6.12/784-09-v6.19-net-phy-realtek-eliminate-priv-phycr2-variable.patch @@ -0,0 +1,101 @@ +From 27033d06917758d47162581da7e9de8004049dee Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 18 Nov 2025 01:40:29 +0200 +Subject: [PATCH] net: phy: realtek: eliminate priv->phycr2 variable + +The RTL8211F(D)(I)-VD-CG PHY also has support for disabling the CLKOUT, +and we'd like to introduce the "realtek,clkout-disable" property for +that. + +But it isn't done through the PHYCR2 register, and it becomes awkward to +have the driver pretend that it is. So just replace the machine-level +"u16 phycr2" variable with a logical "bool disable_clk_out", which +scales better to the other PHY as well. + +The change is a complete functional equivalent. Before, if the device +tree property was absent, priv->phycr2 would contain the RTL8211F_CLKOUT_EN +bit as read from hardware. Now, we don't save priv->phycr2, but we just +don't call phy_modify_paged() on it. Also, we can simply call +phy_modify_paged() with the "set" argument to 0. + +Signed-off-by: Vladimir Oltean +Link: https://patch.msgid.link/20251117234033.345679-3-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 38 ++++++++++++++++---------- + 1 file changed, 23 insertions(+), 15 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -194,8 +194,8 @@ MODULE_LICENSE("GPL"); + + struct rtl821x_priv { + u16 phycr1; +- u16 phycr2; + bool has_phycr2; ++ bool disable_clk_out; + struct clk *clk; + /* rtl8211f */ + u16 iner; +@@ -266,15 +266,8 @@ static int rtl821x_probe(struct phy_devi + priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF; + + priv->has_phycr2 = !(phy_id == RTL_8211FVD_PHYID); +- if (priv->has_phycr2) { +- ret = phy_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2); +- if (ret < 0) +- return ret; +- +- priv->phycr2 = ret & RTL8211F_CLKOUT_EN; +- if (of_property_read_bool(dev->of_node, "realtek,clkout-disable")) +- priv->phycr2 &= ~RTL8211F_CLKOUT_EN; +- } ++ priv->disable_clk_out = of_property_read_bool(dev->of_node, ++ "realtek,clkout-disable"); + + phydev->priv = priv; + +@@ -654,6 +647,23 @@ static int rtl8211f_config_rgmii_delay(s + return 0; + } + ++static int rtl8211f_config_clk_out(struct phy_device *phydev) ++{ ++ struct rtl821x_priv *priv = phydev->priv; ++ int ret; ++ ++ /* The value is preserved if the device tree property is absent */ ++ if (!priv->disable_clk_out) ++ return 0; ++ ++ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, ++ RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, 0); ++ if (ret) ++ return ret; ++ ++ return genphy_soft_reset(phydev); ++} ++ + static int rtl8211f_config_init(struct phy_device *phydev) + { + struct rtl821x_priv *priv = phydev->priv; +@@ -682,16 +692,14 @@ static int rtl8211f_config_init(struct p + if (ret) + return ret; + +- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, +- RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, +- priv->phycr2); +- if (ret < 0) { ++ ret = rtl8211f_config_clk_out(phydev); ++ if (ret) { + dev_err(dev, "clkout configuration failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + +- return genphy_soft_reset(phydev); ++ return 0; + } + + static int rtl821x_suspend(struct phy_device *phydev) diff --git a/target/linux/generic/backport-6.12/784-10-v6.19-net-phy-realtek-eliminate-has_phycr2-variable.patch b/target/linux/generic/backport-6.12/784-10-v6.19-net-phy-realtek-eliminate-has_phycr2-variable.patch new file mode 100644 index 00000000000..a78cb3d1cc7 --- /dev/null +++ b/target/linux/generic/backport-6.12/784-10-v6.19-net-phy-realtek-eliminate-has_phycr2-variable.patch @@ -0,0 +1,55 @@ +From 910ac7bfb1af1ae4cd141ef80e03a6729213c189 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 18 Nov 2025 01:40:30 +0200 +Subject: [PATCH] net: phy: realtek: eliminate has_phycr2 variable + +This variable is assigned in rtl821x_probe() and used in +rtl8211f_config_init(), which is more complex than it needs to be. +Simply testing the same condition from rtl821x_probe() in +rtl8211f_config_init() yields the same result (the PHY driver ID is a +runtime invariant), but with one temporary variable less. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20251117234033.345679-4-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -194,7 +194,6 @@ MODULE_LICENSE("GPL"); + + struct rtl821x_priv { + u16 phycr1; +- bool has_phycr2; + bool disable_clk_out; + struct clk *clk; + /* rtl8211f */ +@@ -245,7 +244,6 @@ static int rtl821x_probe(struct phy_devi + { + struct device *dev = &phydev->mdio.dev; + struct rtl821x_priv *priv; +- u32 phy_id = phydev->drv->phy_id; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +@@ -265,7 +263,6 @@ static int rtl821x_probe(struct phy_devi + if (of_property_read_bool(dev->of_node, "realtek,aldps-enable")) + priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF; + +- priv->has_phycr2 = !(phy_id == RTL_8211FVD_PHYID); + priv->disable_clk_out = of_property_read_bool(dev->of_node, + "realtek,clkout-disable"); + +@@ -683,7 +680,8 @@ static int rtl8211f_config_init(struct p + if (ret) + return ret; + +- if (!priv->has_phycr2) ++ /* RTL8211FVD has no PHYCR2 register */ ++ if (phydev->drv->phy_id == RTL_8211FVD_PHYID) + return 0; + + /* Disable PHY-mode EEE so LPI is passed to the MAC */ diff --git a/target/linux/generic/backport-6.12/784-11-v6.19-net-phy-realtek-allow-CLKOUT-to-be-disabled-on-RTL82.patch b/target/linux/generic/backport-6.12/784-11-v6.19-net-phy-realtek-allow-CLKOUT-to-be-disabled-on-RTL82.patch new file mode 100644 index 00000000000..2a3239d4a2e --- /dev/null +++ b/target/linux/generic/backport-6.12/784-11-v6.19-net-phy-realtek-allow-CLKOUT-to-be-disabled-on-RTL82.patch @@ -0,0 +1,101 @@ +From e1a31c41bef678afe0d99b7f0dc3711a80c68447 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 18 Nov 2025 01:40:31 +0200 +Subject: [PATCH] net: phy: realtek: allow CLKOUT to be disabled on + RTL8211F(D)(I)-VD-CG + +Add CLKOUT disable support for RTL8211F(D)(I)-VD-CG. Like with other PHY +variants, this feature might be requested by customers when the clock +output is not used, in order to reduce electromagnetic interference (EMI). + +In the common driver, the CLKOUT configuration is done through PHYCR2. +The RTL_8211FVD_PHYID is singled out as not having that register, and +execution in rtl8211f_config_init() returns early after commit +2c67301584f2 ("net: phy: realtek: Avoid PHYCR2 access if PHYCR2 not +present"). + +But actually CLKOUT is configured through a different register for this +PHY. Instead of pretending this is PHYCR2 (which it is not), just add +some code for modifying this register inside the rtl8211f_disable_clk_out() +function, and move that outside the code portion that runs only if +PHYCR2 exists. + +In practice this reorders the PHYCR2 writes to disable PHY-mode EEE and +to disable the CLKOUT for the normal RTL8211F variants, but this should +be perfectly fine. + +It was not noted that RTL8211F(D)(I)-VD-CG would need a genphy_soft_reset() +call after disabling the CLKOUT. Despite that, we do it out of caution +and for symmetry with the other RTL8211F models. + +Co-developed-by: Clark Wang +Signed-off-by: Clark Wang +Signed-off-by: Vladimir Oltean +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20251117234033.345679-5-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 31 ++++++++++++++++++-------- + 1 file changed, 22 insertions(+), 9 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -90,6 +90,14 @@ + #define RTL8211F_LEDCR_MASK GENMASK(4, 0) + #define RTL8211F_LEDCR_SHIFT 5 + ++/* RTL8211F(D)(I)-VD-CG CLKOUT configuration is specified via magic values ++ * to undocumented register pages. The names here do not reflect the datasheet. ++ * Unlike other PHY models, CLKOUT configuration does not go through PHYCR2. ++ */ ++#define RTL8211FVD_CLKOUT_PAGE 0xd05 ++#define RTL8211FVD_CLKOUT_REG 0x11 ++#define RTL8211FVD_CLKOUT_EN BIT(8) ++ + /* RTL8211F RGMII configuration */ + #define RTL8211F_RGMII_PAGE 0xd08 + +@@ -653,8 +661,13 @@ static int rtl8211f_config_clk_out(struc + if (!priv->disable_clk_out) + return 0; + +- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, +- RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, 0); ++ if (phydev->drv->phy_id == RTL_8211FVD_PHYID) ++ ret = phy_modify_paged(phydev, RTL8211FVD_CLKOUT_PAGE, ++ RTL8211FVD_CLKOUT_REG, ++ RTL8211FVD_CLKOUT_EN, 0); ++ else ++ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, ++ RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, 0); + if (ret) + return ret; + +@@ -680,6 +693,13 @@ static int rtl8211f_config_init(struct p + if (ret) + return ret; + ++ ret = rtl8211f_config_clk_out(phydev); ++ if (ret) { ++ dev_err(dev, "clkout configuration failed: %pe\n", ++ ERR_PTR(ret)); ++ return ret; ++ } ++ + /* RTL8211FVD has no PHYCR2 register */ + if (phydev->drv->phy_id == RTL_8211FVD_PHYID) + return 0; +@@ -690,13 +710,6 @@ static int rtl8211f_config_init(struct p + if (ret) + return ret; + +- ret = rtl8211f_config_clk_out(phydev); +- if (ret) { +- dev_err(dev, "clkout configuration failed: %pe\n", +- ERR_PTR(ret)); +- return ret; +- } +- + return 0; + } + diff --git a/target/linux/generic/backport-6.12/784-12-v6.19-net-phy-realtek-eliminate-priv-phycr1-variable.patch b/target/linux/generic/backport-6.12/784-12-v6.19-net-phy-realtek-eliminate-priv-phycr1-variable.patch new file mode 100644 index 00000000000..a2e5abe93f4 --- /dev/null +++ b/target/linux/generic/backport-6.12/784-12-v6.19-net-phy-realtek-eliminate-priv-phycr1-variable.patch @@ -0,0 +1,107 @@ +From bb78b71faf60d11a15f07e3390fcfd31e5e523bb Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 18 Nov 2025 01:40:32 +0200 +Subject: [PATCH] net: phy: realtek: eliminate priv->phycr1 variable + +Previous changes have replaced the machine-level priv->phycr2 with a +high-level priv->disable_clk_out. This created a discrepancy with +priv->phycr1 which is resolved here, for uniformity. + +One advantage of this new implementation is that we don't read +priv->phycr1 in rtl821x_probe() if we're never going to modify it. + +We never test the positive return code from phy_modify_mmd_changed(), so +we could just as well use phy_modify_mmd(). + +I took the ALDPS feature description from commit d90db36a9e74 ("net: +phy: realtek: add dt property to enable ALDPS mode") and transformed it +into a function comment - the feature is sufficiently non-obvious to +deserve that. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20251117234033.345679-6-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 44 ++++++++++++++++---------- + 1 file changed, 28 insertions(+), 16 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -201,7 +201,7 @@ MODULE_AUTHOR("Johnson Leung"); + MODULE_LICENSE("GPL"); + + struct rtl821x_priv { +- u16 phycr1; ++ bool enable_aldps; + bool disable_clk_out; + struct clk *clk; + /* rtl8211f */ +@@ -252,7 +252,6 @@ static int rtl821x_probe(struct phy_devi + { + struct device *dev = &phydev->mdio.dev; + struct rtl821x_priv *priv; +- int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -263,14 +262,8 @@ static int rtl821x_probe(struct phy_devi + return dev_err_probe(dev, PTR_ERR(priv->clk), + "failed to get phy clock\n"); + +- ret = phy_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1); +- if (ret < 0) +- return ret; +- +- priv->phycr1 = ret & (RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF); +- if (of_property_read_bool(dev->of_node, "realtek,aldps-enable")) +- priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF; +- ++ priv->enable_aldps = of_property_read_bool(dev->of_node, ++ "realtek,aldps-enable"); + priv->disable_clk_out = of_property_read_bool(dev->of_node, + "realtek,clkout-disable"); + +@@ -674,17 +667,36 @@ static int rtl8211f_config_clk_out(struc + return genphy_soft_reset(phydev); + } + +-static int rtl8211f_config_init(struct phy_device *phydev) ++/* Advance Link Down Power Saving (ALDPS) mode changes crystal/clock behaviour, ++ * which causes the RXC clock signal to stop for tens to hundreds of ++ * milliseconds. ++ * ++ * Some MACs need the RXC clock to support their internal RX logic, so ALDPS is ++ * only enabled based on an opt-in device tree property. ++ */ ++static int rtl8211f_config_aldps(struct phy_device *phydev) + { + struct rtl821x_priv *priv = phydev->priv; ++ u16 mask = RTL8211F_ALDPS_PLL_OFF | ++ RTL8211F_ALDPS_ENABLE | ++ RTL8211F_ALDPS_XTAL_OFF; ++ ++ /* The value is preserved if the device tree property is absent */ ++ if (!priv->enable_aldps) ++ return 0; ++ ++ return phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1, ++ mask, mask); ++} ++ ++static int rtl8211f_config_init(struct phy_device *phydev) ++{ + struct device *dev = &phydev->mdio.dev; + int ret; + +- ret = phy_modify_paged_changed(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1, +- RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF, +- priv->phycr1); +- if (ret < 0) { +- dev_err(dev, "aldps mode configuration failed: %pe\n", ++ ret = rtl8211f_config_aldps(phydev); ++ if (ret) { ++ dev_err(dev, "aldps mode configuration failed: %pe\n", + ERR_PTR(ret)); + return ret; + } diff --git a/target/linux/generic/backport-6.12/784-13-v6.19-net-phy-realtek-create-rtl8211f_config_phy_eee-helpe.patch b/target/linux/generic/backport-6.12/784-13-v6.19-net-phy-realtek-create-rtl8211f_config_phy_eee-helpe.patch new file mode 100644 index 00000000000..54ec217aff1 --- /dev/null +++ b/target/linux/generic/backport-6.12/784-13-v6.19-net-phy-realtek-create-rtl8211f_config_phy_eee-helpe.patch @@ -0,0 +1,58 @@ +From 4465ae435ddc0162d5033a543658449d53d46d08 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Tue, 18 Nov 2025 01:40:33 +0200 +Subject: [PATCH] net: phy: realtek: create rtl8211f_config_phy_eee() helper + +To simplify the rtl8211f_config_init() control flow and get rid of +"early" returns for PHYs where the PHYCR2 register is absent, move the +entire logic sub-block that deals with disabling PHY-mode EEE to a +separate function. There, it is much more obvious what the early +"return 0" skips, and it becomes more difficult to accidentally skip +unintended stuff. + +Signed-off-by: Vladimir Oltean +Link: https://patch.msgid.link/20251117234033.345679-7-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -689,6 +689,17 @@ static int rtl8211f_config_aldps(struct + mask, mask); + } + ++static int rtl8211f_config_phy_eee(struct phy_device *phydev) ++{ ++ /* RTL8211FVD has no PHYCR2 register */ ++ if (phydev->drv->phy_id == RTL_8211FVD_PHYID) ++ return 0; ++ ++ /* Disable PHY-mode EEE so LPI is passed to the MAC */ ++ return phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2, ++ RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0); ++} ++ + static int rtl8211f_config_init(struct phy_device *phydev) + { + struct device *dev = &phydev->mdio.dev; +@@ -712,17 +723,7 @@ static int rtl8211f_config_init(struct p + return ret; + } + +- /* RTL8211FVD has no PHYCR2 register */ +- if (phydev->drv->phy_id == RTL_8211FVD_PHYID) +- return 0; +- +- /* Disable PHY-mode EEE so LPI is passed to the MAC */ +- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2, +- RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0); +- if (ret) +- return ret; +- +- return 0; ++ return rtl8211f_config_phy_eee(phydev); + } + + static int rtl821x_suspend(struct phy_device *phydev) diff --git a/target/linux/generic/backport-6.12/785-v6.18-r8169-fix-RTL8127-hang-on-suspend-shutdown.patch b/target/linux/generic/backport-6.12/785-v6.18-r8169-fix-RTL8127-hang-on-suspend-shutdown.patch new file mode 100644 index 00000000000..269fc89d261 --- /dev/null +++ b/target/linux/generic/backport-6.12/785-v6.18-r8169-fix-RTL8127-hang-on-suspend-shutdown.patch @@ -0,0 +1,50 @@ +From ae1737e7339b513f8c2fc21b500a0fc215d155c3 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Sat, 22 Nov 2025 15:23:02 +0100 +Subject: r8169: fix RTL8127 hang on suspend/shutdown + +There have been reports that RTL8127 hangs on suspend and shutdown, +partially disappearing from lspci until power-cycling. +According to Realtek disabling PLL's when switching to D3 should be +avoided on that chip version. Fix this by aligning disabling PLL's +with the vendor drivers, what in addition results in PLL's not being +disabled when switching to D3hot on other chip versions. + +Fixes: f24f7b2f3af9 ("r8169: add support for RTL8127A") +Tested-by: Fabio Baltieri +Cc: stable@vger.kernel.org +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/d7faae7e-66bc-404a-a432-3a496600575f@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/realtek/r8169_main.c | 19 ++++++++++++++----- + 1 file changed, 14 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -1516,11 +1516,20 @@ static enum rtl_dash_type rtl_get_dash_t + + static void rtl_set_d3_pll_down(struct rtl8169_private *tp, bool enable) + { +- if (tp->mac_version >= RTL_GIGA_MAC_VER_25 && +- tp->mac_version != RTL_GIGA_MAC_VER_28 && +- tp->mac_version != RTL_GIGA_MAC_VER_31 && +- tp->mac_version != RTL_GIGA_MAC_VER_38) +- r8169_mod_reg8_cond(tp, PMCH, D3_NO_PLL_DOWN, !enable); ++ switch (tp->mac_version) { ++ case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_24: ++ case RTL_GIGA_MAC_VER_28: ++ case RTL_GIGA_MAC_VER_31: ++ case RTL_GIGA_MAC_VER_38: ++ break; ++ case RTL_GIGA_MAC_VER_80: ++ r8169_mod_reg8_cond(tp, PMCH, D3_NO_PLL_DOWN, true); ++ break; ++ default: ++ r8169_mod_reg8_cond(tp, PMCH, D3HOT_NO_PLL_DOWN, true); ++ r8169_mod_reg8_cond(tp, PMCH, D3COLD_NO_PLL_DOWN, !enable); ++ break; ++ } + } + + static void rtl_reset_packet_filter(struct rtl8169_private *tp) diff --git a/target/linux/generic/backport-6.12/786-01-v6.18-net-phy-introduce-phy_id_compare_vendor-PHY-ID-helpe.patch b/target/linux/generic/backport-6.12/786-01-v6.18-net-phy-introduce-phy_id_compare_vendor-PHY-ID-helpe.patch new file mode 100644 index 00000000000..a69a5641485 --- /dev/null +++ b/target/linux/generic/backport-6.12/786-01-v6.18-net-phy-introduce-phy_id_compare_vendor-PHY-ID-helpe.patch @@ -0,0 +1,58 @@ +From 1abe21ef1adf0c5b6dbb5878c2fa4573df8d29fc Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Aug 2025 15:44:28 +0200 +Subject: net: phy: introduce phy_id_compare_vendor() PHY ID helper + +Introduce phy_id_compare_vendor() PHY ID helper to compare a PHY ID with +the PHY ID Vendor using the generic PHY ID Vendor mask. + +While at it also rework the PHY_ID_MATCH macro and move the mask to +dedicated define so that PHY driver can make use of the mask if needed. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250823134431.4854-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + include/linux/phy.h | 23 ++++++++++++++++++++--- + 1 file changed, 20 insertions(+), 3 deletions(-) + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1266,9 +1266,13 @@ struct phy_driver { + #define PHY_ANY_ID "MATCH ANY PHY" + #define PHY_ANY_UID 0xffffffff + +-#define PHY_ID_MATCH_EXACT(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 0) +-#define PHY_ID_MATCH_MODEL(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 4) +-#define PHY_ID_MATCH_VENDOR(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 10) ++#define PHY_ID_MATCH_EXTACT_MASK GENMASK(31, 0) ++#define PHY_ID_MATCH_MODEL_MASK GENMASK(31, 4) ++#define PHY_ID_MATCH_VENDOR_MASK GENMASK(31, 10) ++ ++#define PHY_ID_MATCH_EXACT(id) .phy_id = (id), .phy_id_mask = PHY_ID_MATCH_EXTACT_MASK ++#define PHY_ID_MATCH_MODEL(id) .phy_id = (id), .phy_id_mask = PHY_ID_MATCH_MODEL_MASK ++#define PHY_ID_MATCH_VENDOR(id) .phy_id = (id), .phy_id_mask = PHY_ID_MATCH_VENDOR_MASK + + /** + * phy_id_compare - compare @id1 with @id2 taking account of @mask +@@ -1298,6 +1302,19 @@ static inline bool phy_id_compare_model( + } + + /** ++ * phy_id_compare_vendor - compare @id with @vendor mask ++ * @id: PHY ID ++ * @vendor_mask: PHY Vendor mask ++ * ++ * Return: true if the bits from @id match @vendor using the ++ * generic PHY Vendor mask. ++ */ ++static inline bool phy_id_compare_vendor(u32 id, u32 vendor_mask) ++{ ++ return phy_id_compare(id, vendor_mask, PHY_ID_MATCH_VENDOR_MASK); ++} ++ ++/** + * phydev_id_compare - compare @id with the PHY's Clause 22 ID + * @phydev: the PHY device + * @id: the PHY ID to be matched diff --git a/target/linux/generic/backport-6.12/786-02-v6.18-net-phy-as21xxx-better-handle-PHY-HW-reset-on-soft-r.patch b/target/linux/generic/backport-6.12/786-02-v6.18-net-phy-as21xxx-better-handle-PHY-HW-reset-on-soft-r.patch new file mode 100644 index 00000000000..5b00da40242 --- /dev/null +++ b/target/linux/generic/backport-6.12/786-02-v6.18-net-phy-as21xxx-better-handle-PHY-HW-reset-on-soft-r.patch @@ -0,0 +1,45 @@ +From b4d5cd20507b252c746fa6971d82ac96f3b3e5b7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Aug 2025 15:44:29 +0200 +Subject: net: phy: as21xxx: better handle PHY HW reset on soft-reboot + +On soft-reboot, with a reset GPIO defined for an Aeonsemi PHY, the +special match_phy_device fails to correctly identify that the PHY +needs to load the firmware again. + +This is caused by the fact that PHY ID is read BEFORE the PHY reset +GPIO (if present) is asserted, so we can be in the scenario where the +phydev have the previous PHY ID (with the PHY firmware loaded) but +after reset the generic AS21xxx PHY is present in the PHY ID registers. + +To better handle this, skip reading the PHY ID register only for the PHY +that are not AS21xxx (by matching for the Aeonsemi Vendor) and always +read the PHY ID for the other case to handle both firmware already +loaded or an HW reset. + +Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs") +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250823134431.4854-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/as21xxx.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/as21xxx.c ++++ b/drivers/net/phy/as21xxx.c +@@ -884,11 +884,12 @@ static int as21xxx_match_phy_device(stru + u32 phy_id; + int ret; + +- /* Skip PHY that are not AS21xxx or already have firmware loaded */ +- if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX) ++ /* Skip PHY that are not AS21xxx */ ++ if (!phy_id_compare_vendor(phydev->c45_ids.device_ids[MDIO_MMD_PCS], ++ PHY_VENDOR_AEONSEMI)) + return genphy_match_phy_device(phydev, phydrv); + +- /* Read PHY ID to handle firmware just loaded */ ++ /* Read PHY ID to handle firmware loaded or HW reset */ + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1); + if (ret < 0) + return ret; diff --git a/target/linux/generic/backport-6.12/787-v6.19-net-phy-RTL8211FVD-Restore-disabling-of-PHY-mode-EEE.patch b/target/linux/generic/backport-6.12/787-v6.19-net-phy-RTL8211FVD-Restore-disabling-of-PHY-mode-EEE.patch new file mode 100644 index 00000000000..37290c4e5d2 --- /dev/null +++ b/target/linux/generic/backport-6.12/787-v6.19-net-phy-RTL8211FVD-Restore-disabling-of-PHY-mode-EEE.patch @@ -0,0 +1,61 @@ +From 4f0638b12451112de4138689fa679315c8d388dc Mon Sep 17 00:00:00 2001 +From: Ivan Galkin +Date: Tue, 2 Dec 2025 10:07:42 +0100 +Subject: net: phy: RTL8211FVD: Restore disabling of PHY-mode EEE + +When support for RTL8211F(D)(I)-VD-CG was introduced in commit +bb726b753f75 ("net: phy: realtek: add support for RTL8211F(D)(I)-VD-CG") +the implementation assumed that this PHY model doesn't have the +control register PHYCR2 (Page 0xa43 Address 0x19). This +assumption was based on the differences in CLKOUT configurations +between RTL8211FVD and the remaining RTL8211F PHYs. In the latter +commit 2c67301584f2 +("net: phy: realtek: Avoid PHYCR2 access if PHYCR2 not present") +this assumption was expanded to the PHY-mode EEE. + +I performed tests on RTL8211FI-VD-CG and confirmed that disabling +PHY-mode EEE works correctly and is uniform with other PHYs +supported by the driver. To validate the correctness, +I contacted Realtek support. Realtek confirmed that PHY-mode EEE on +RTL8211F(D)(I)-VD-CG is configured via Page 0xa43 Address 0x19 bit 5. + +Moreover, Realtek informed me that the most recent datasheet +for RTL8211F(D)(I)-VD-CG v1.1 is incomplete and the naming of +control registers is partly inconsistent. The errata I +received from Realtek corrects the naming as follows: + +| Register | Datasheet v1.1 | Errata | +|-------------------------|----------------|--------| +| Page 0xa44 Address 0x11 | PHYCR2 | PHYCR3 | +| Page 0xa43 Address 0x19 | N/A | PHYCR2 | + +This information confirms that the supposedly missing control register, +PHYCR2, exists in the RTL8211F(D)(I)-VD-CG under the same address and +the same name. It controls widely the same configs as other PHYs from +the RTL8211F series (e.g. PHY-mode EEE). Clock out configuration is an +exception. + +Given all this information, restore disabling of the PHY-mode EEE. + +Fixes: 2c67301584f2 ("net: phy: realtek: Avoid PHYCR2 access if PHYCR2 not present") +Signed-off-by: Ivan Galkin +Reviewed-by: Vladimir Oltean +Link: https://patch.msgid.link/20251202-phy_eee-v1-1-fe0bf6ab3df0@axis.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -691,10 +691,6 @@ static int rtl8211f_config_aldps(struct + + static int rtl8211f_config_phy_eee(struct phy_device *phydev) + { +- /* RTL8211FVD has no PHYCR2 register */ +- if (phydev->drv->phy_id == RTL_8211FVD_PHYID) +- return 0; +- + /* Disable PHY-mode EEE so LPI is passed to the MAC */ + return phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2, + RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0); diff --git a/target/linux/generic/backport-6.12/788-v7.0-net-phy-realtek-fix-whitespace-in-struct-phy_driver-.patch b/target/linux/generic/backport-6.12/788-v7.0-net-phy-realtek-fix-whitespace-in-struct-phy_driver-.patch new file mode 100644 index 00000000000..e577166e904 --- /dev/null +++ b/target/linux/generic/backport-6.12/788-v7.0-net-phy-realtek-fix-whitespace-in-struct-phy_driver-.patch @@ -0,0 +1,229 @@ +From 50326b48f0cfbd9324668c5d2e08b39b59dc199f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 5 Jan 2026 16:37:54 +0000 +Subject: [PATCH 1/5] net: phy: realtek: fix whitespace in struct phy_driver + initializers + +Consistently use tabs instead of spaces in struct phy_driver +initializers. + +Signed-off-by: Daniel Golle +Reviewed-by: Maxime Chevallier +Link: https://patch.msgid.link/42b0fac53c5c5646707ce3f3a6dacd2bc082a5b2.1767630451.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 146 ++++++++++++------------- + 1 file changed, 73 insertions(+), 73 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1976,7 +1976,7 @@ static irqreturn_t rtl8221b_handle_inter + static struct phy_driver realtek_drvs[] = { + { + PHY_ID_MATCH_EXACT(0x00008201), +- .name = "RTL8201CP Ethernet", ++ .name = "RTL8201CP Ethernet", + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + }, { +@@ -2102,7 +2102,7 @@ static struct phy_driver realtek_drvs[] + .name = "RTL8226B_RTL8221B 2.5Gbps PHY", + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, +- .config_init = rtl822xb_config_init, ++ .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, + .read_status = rtl822xb_read_status, + .suspend = genphy_suspend, +@@ -2111,112 +2111,112 @@ static struct phy_driver realtek_drvs[] + .write_page = rtl821x_write_page, + }, { + PHY_ID_MATCH_EXACT(0x001cc838), +- .name = "RTL8226-CG 2.5Gbps PHY", +- .soft_reset = rtl822x_c45_soft_reset, +- .get_features = rtl822x_c45_get_features, +- .config_aneg = rtl822x_c45_config_aneg, +- .config_init = rtl822x_config_init, +- .read_status = rtl822xb_c45_read_status, +- .suspend = genphy_c45_pma_suspend, +- .resume = rtlgen_c45_resume, ++ .name = "RTL8226-CG 2.5Gbps PHY", ++ .soft_reset = rtl822x_c45_soft_reset, ++ .get_features = rtl822x_c45_get_features, ++ .config_aneg = rtl822x_c45_config_aneg, ++ .config_init = rtl822x_config_init, ++ .read_status = rtl822xb_c45_read_status, ++ .suspend = genphy_c45_pma_suspend, ++ .resume = rtlgen_c45_resume, + }, { + PHY_ID_MATCH_EXACT(0x001cc848), +- .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .config_init = rtl822xb_config_init, ++ .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, +- .read_status = rtl822xb_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, ++ .read_status = rtl822xb_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, + }, { + .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, +- .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", ++ .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", + .probe = rtl822x_probe, +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .config_init = rtl822xb_config_init, ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, +- .read_status = rtl822xb_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, ++ .read_status = rtl822xb_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, + }, { + .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, +- .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", ++ .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", + .config_intr = rtl8221b_config_intr, + .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, +- .config_init = rtl822xb_config_init, ++ .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, +- .get_features = rtl822x_c45_get_features, +- .config_aneg = rtl822x_c45_config_aneg, +- .read_status = rtl822xb_c45_read_status, +- .suspend = genphy_c45_pma_suspend, +- .resume = rtlgen_c45_resume, ++ .get_features = rtl822x_c45_get_features, ++ .config_aneg = rtl822x_c45_config_aneg, ++ .read_status = rtl822xb_c45_read_status, ++ .suspend = genphy_c45_pma_suspend, ++ .resume = rtlgen_c45_resume, + }, { + .match_phy_device = rtl8221b_vm_cg_c22_match_phy_device, +- .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", ++ .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", + .probe = rtl822x_probe, +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .config_init = rtl822xb_config_init, ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, +- .read_status = rtl822xb_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, ++ .read_status = rtl822xb_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, + }, { + .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device, +- .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)", ++ .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)", + .config_intr = rtl8221b_config_intr, + .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, +- .config_init = rtl822xb_config_init, ++ .config_init = rtl822xb_config_init, + .get_rate_matching = rtl822xb_get_rate_matching, +- .get_features = rtl822x_c45_get_features, +- .config_aneg = rtl822x_c45_config_aneg, +- .read_status = rtl822xb_c45_read_status, +- .suspend = genphy_c45_pma_suspend, +- .resume = rtlgen_c45_resume, ++ .get_features = rtl822x_c45_get_features, ++ .config_aneg = rtl822x_c45_config_aneg, ++ .read_status = rtl822xb_c45_read_status, ++ .suspend = genphy_c45_pma_suspend, ++ .resume = rtlgen_c45_resume, + }, { + .match_phy_device = rtl8251b_c45_match_phy_device, +- .name = "RTL8251B 5Gbps PHY", ++ .name = "RTL8251B 5Gbps PHY", + .probe = rtl822x_probe, +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .read_status = rtl822x_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .read_status = rtl822x_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, + }, { + .match_phy_device = rtl_internal_nbaset_match_phy_device, +- .name = "Realtek Internal NBASE-T PHY", ++ .name = "Realtek Internal NBASE-T PHY", + .flags = PHY_IS_INTERNAL, + .probe = rtl822x_probe, +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .read_status = rtl822x_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, ++ .get_features = rtl822x_get_features, ++ .config_aneg = rtl822x_config_aneg, ++ .read_status = rtl822x_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, + .read_mmd = rtl822x_read_mmd, + .write_mmd = rtl822x_write_mmd, + }, { + PHY_ID_MATCH_EXACT(0x001ccad0), + .name = "RTL8224 2.5Gbps PHY", + .flags = PHY_POLL_CABLE_TEST, +- .get_features = rtl822x_c45_get_features, +- .config_aneg = rtl822x_c45_config_aneg, +- .read_status = rtl822x_c45_read_status, +- .suspend = genphy_c45_pma_suspend, +- .resume = rtlgen_c45_resume, ++ .get_features = rtl822x_c45_get_features, ++ .config_aneg = rtl822x_c45_config_aneg, ++ .read_status = rtl822x_c45_read_status, ++ .suspend = genphy_c45_pma_suspend, ++ .resume = rtlgen_c45_resume, + .cable_test_start = rtl8224_cable_test_start, + .cable_test_get_status = rtl8224_cable_test_get_status, + }, { +@@ -2235,7 +2235,7 @@ static struct phy_driver realtek_drvs[] + }, { + PHY_ID_MATCH_EXACT(0x001ccb00), + .name = "RTL9000AA_RTL9000AN Ethernet", +- .features = PHY_BASIC_T1_FEATURES, ++ .features = PHY_BASIC_T1_FEATURES, + .config_init = rtl9000a_config_init, + .config_aneg = rtl9000a_config_aneg, + .read_status = rtl9000a_read_status, diff --git a/target/linux/generic/backport-6.12/789-v7.0-net-phy-realtek-implement-configuring-in-band-an.patch b/target/linux/generic/backport-6.12/789-v7.0-net-phy-realtek-implement-configuring-in-band-an.patch new file mode 100644 index 00000000000..16e6de29a7e --- /dev/null +++ b/target/linux/generic/backport-6.12/789-v7.0-net-phy-realtek-implement-configuring-in-band-an.patch @@ -0,0 +1,148 @@ +From 10fbd71fc5f9bef5018a35103d493307683ddca1 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 5 Jan 2026 16:38:12 +0000 +Subject: [PATCH 2/5] net: phy: realtek: implement configuring in-band an + +Implement the inband_caps() and config_inband() PHY driver methods to +allow configuring the use of in-band-status with SGMII and 2500Base-X on +RTL8226 and RTL8221B 2.5GE PHYs. + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/82a78a06d67be19e856d646cf880b2021ea9d837.1767630451.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 67 ++++++++++++++++++++++++++ + 1 file changed, 67 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -131,6 +131,15 @@ + #define RTL822X_VND1_SERDES_CTRL3_MODE_SGMII 0x02 + #define RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX 0x16 + ++#define RTL822X_VND1_SERDES_CMD 0x7587 ++#define RTL822X_VND1_SERDES_CMD_WRITE BIT(1) ++#define RTL822X_VND1_SERDES_CMD_BUSY BIT(0) ++#define RTL822X_VND1_SERDES_ADDR 0x7588 ++#define RTL822X_VND1_SERDES_ADDR_AUTONEG 0x2 ++#define RTL822X_VND1_SERDES_INBAND_DISABLE 0x71d0 ++#define RTL822X_VND1_SERDES_INBAND_ENABLE 0x70d0 ++#define RTL822X_VND1_SERDES_DATA 0x7589 ++ + /* RTL822X_VND2_XXXXX registers are only accessible when phydev->is_c45 + * is set, they cannot be accessed by C45-over-C22. + */ +@@ -1308,6 +1317,50 @@ static int rtl822xb_config_init(struct p + return rtl822x_set_serdes_option_mode(phydev, false); + } + ++static int rtl822x_serdes_write(struct phy_device *phydev, u16 reg, u16 val) ++{ ++ int ret, poll; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_ADDR, reg); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_DATA, val); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_CMD, ++ RTL822X_VND1_SERDES_CMD_WRITE | ++ RTL822X_VND1_SERDES_CMD_BUSY); ++ if (ret < 0) ++ return ret; ++ ++ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ RTL822X_VND1_SERDES_CMD, poll, ++ !(poll & RTL822X_VND1_SERDES_CMD_BUSY), ++ 500, 100000, false); ++} ++ ++static int rtl822x_config_inband(struct phy_device *phydev, unsigned int modes) ++{ ++ return rtl822x_serdes_write(phydev, RTL822X_VND1_SERDES_ADDR_AUTONEG, ++ (modes != LINK_INBAND_DISABLE) ? ++ RTL822X_VND1_SERDES_INBAND_ENABLE : ++ RTL822X_VND1_SERDES_INBAND_DISABLE); ++} ++ ++static unsigned int rtl822x_inband_caps(struct phy_device *phydev, ++ phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_SGMII: ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; ++ default: ++ return 0; ++ } ++} ++ + static int rtl822xb_get_rate_matching(struct phy_device *phydev, + phy_interface_t iface) + { +@@ -2103,6 +2156,8 @@ static struct phy_driver realtek_drvs[] + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, ++ .inband_caps = rtl822x_inband_caps, ++ .config_inband = rtl822x_config_inband, + .get_rate_matching = rtl822xb_get_rate_matching, + .read_status = rtl822xb_read_status, + .suspend = genphy_suspend, +@@ -2116,6 +2171,8 @@ static struct phy_driver realtek_drvs[] + .get_features = rtl822x_c45_get_features, + .config_aneg = rtl822x_c45_config_aneg, + .config_init = rtl822x_config_init, ++ .inband_caps = rtl822x_inband_caps, ++ .config_inband = rtl822x_config_inband, + .read_status = rtl822xb_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, +@@ -2125,6 +2182,8 @@ static struct phy_driver realtek_drvs[] + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, ++ .inband_caps = rtl822x_inband_caps, ++ .config_inband = rtl822x_config_inband, + .get_rate_matching = rtl822xb_get_rate_matching, + .read_status = rtl822xb_read_status, + .suspend = genphy_suspend, +@@ -2138,6 +2197,8 @@ static struct phy_driver realtek_drvs[] + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, ++ .inband_caps = rtl822x_inband_caps, ++ .config_inband = rtl822x_config_inband, + .get_rate_matching = rtl822xb_get_rate_matching, + .read_status = rtl822xb_read_status, + .suspend = genphy_suspend, +@@ -2151,6 +2212,8 @@ static struct phy_driver realtek_drvs[] + .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, + .config_init = rtl822xb_config_init, ++ .inband_caps = rtl822x_inband_caps, ++ .config_inband = rtl822x_config_inband, + .get_rate_matching = rtl822xb_get_rate_matching, + .get_features = rtl822x_c45_get_features, + .config_aneg = rtl822x_c45_config_aneg, +@@ -2164,6 +2227,8 @@ static struct phy_driver realtek_drvs[] + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, ++ .inband_caps = rtl822x_inband_caps, ++ .config_inband = rtl822x_config_inband, + .get_rate_matching = rtl822xb_get_rate_matching, + .read_status = rtl822xb_read_status, + .suspend = genphy_suspend, +@@ -2177,6 +2242,8 @@ static struct phy_driver realtek_drvs[] + .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, + .config_init = rtl822xb_config_init, ++ .inband_caps = rtl822x_inband_caps, ++ .config_inband = rtl822x_config_inband, + .get_rate_matching = rtl822xb_get_rate_matching, + .get_features = rtl822x_c45_get_features, + .config_aneg = rtl822x_c45_config_aneg, diff --git a/target/linux/generic/backport-6.12/790-v7.0-net-phy-realtek-use-paged-access-for-MDIO_MMD_VEND2-.patch b/target/linux/generic/backport-6.12/790-v7.0-net-phy-realtek-use-paged-access-for-MDIO_MMD_VEND2-.patch new file mode 100644 index 00000000000..d84fb6af391 --- /dev/null +++ b/target/linux/generic/backport-6.12/790-v7.0-net-phy-realtek-use-paged-access-for-MDIO_MMD_VEND2-.patch @@ -0,0 +1,172 @@ +From 1850ec20d6e71218c02329e5975591075dc972d3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 5 Jan 2026 16:38:59 +0000 +Subject: [PATCH 4/5] net: phy: realtek: use paged access for MDIO_MMD_VEND2 in + C22 mode + +RTL822x cannot access MDIO_MMD_VEND2 via MII_MMD_CTRL/MII_MMD_DATA. A +mapping to use paged access needs to be used instead. All other MMD +devices can be accessed as usual. + +Implement phy_read_mmd and phy_write_mmd using paged access for +MDIO_MMD_VEND2 in Clause-22 mode instead of relying on +MII_MMD_CTRL/MII_MMD_DATA. This allows eg. rtl822x_config_aneg to work +as expected in case the MDIO bus doesn't support Clause-45 access. + +Suggested-by: Bevan Weiss +Signed-off-by: Daniel Golle +Reviewed-by: Maxime Chevallier +Link: https://patch.msgid.link/25aab7f02dac7c6022171455523e3db1435b0881.1767630451.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 91 +++++++++++++++++++++++++- + 1 file changed, 88 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -140,9 +140,8 @@ + #define RTL822X_VND1_SERDES_INBAND_ENABLE 0x70d0 + #define RTL822X_VND1_SERDES_DATA 0x7589 + +-/* RTL822X_VND2_XXXXX registers are only accessible when phydev->is_c45 +- * is set, they cannot be accessed by C45-over-C22. +- */ ++#define RTL822X_VND2_TO_PAGE(reg) ((reg) >> 4) ++#define RTL822X_VND2_TO_PAGE_REG(reg) (16 + (((reg) & GENMASK(3, 0)) >> 1)) + #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg)) + + #define RTL8221B_VND2_INER 0xa4d2 +@@ -1247,6 +1246,79 @@ static int rtl822x_probe(struct phy_devi + return 0; + } + ++/* RTL822x cannot access MDIO_MMD_VEND2 via MII_MMD_CTRL/MII_MMD_DATA. ++ * A mapping to use paged access needs to be used instead. ++ * All other MMD devices can be accessed as usual. ++ */ ++static int rtl822xb_read_mmd(struct phy_device *phydev, int devnum, u16 reg) ++{ ++ int oldpage, ret, read_ret; ++ u16 page; ++ ++ /* Use default method for all MMDs except MDIO_MMD_VEND2 or in case ++ * Clause-45 access is available ++ */ ++ if (devnum != MDIO_MMD_VEND2 || phydev->is_c45) ++ return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr, ++ phydev->is_c45, devnum, reg); ++ ++ /* Use paged access for MDIO_MMD_VEND2 over Clause-22 */ ++ page = RTL822X_VND2_TO_PAGE(reg); ++ oldpage = __phy_read(phydev, RTL821x_PAGE_SELECT); ++ if (oldpage < 0) ++ return oldpage; ++ ++ if (oldpage != page) { ++ ret = __phy_write(phydev, RTL821x_PAGE_SELECT, page); ++ if (ret < 0) ++ return ret; ++ } ++ ++ read_ret = __phy_read(phydev, RTL822X_VND2_TO_PAGE_REG(reg)); ++ if (oldpage != page) { ++ ret = __phy_write(phydev, RTL821x_PAGE_SELECT, oldpage); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return read_ret; ++} ++ ++static int rtl822xb_write_mmd(struct phy_device *phydev, int devnum, u16 reg, ++ u16 val) ++{ ++ int oldpage, ret, write_ret; ++ u16 page; ++ ++ /* Use default method for all MMDs except MDIO_MMD_VEND2 or in case ++ * Clause-45 access is available ++ */ ++ if (devnum != MDIO_MMD_VEND2 || phydev->is_c45) ++ return mmd_phy_write(phydev->mdio.bus, phydev->mdio.addr, ++ phydev->is_c45, devnum, reg, val); ++ ++ /* Use paged access for MDIO_MMD_VEND2 over Clause-22 */ ++ page = RTL822X_VND2_TO_PAGE(reg); ++ oldpage = __phy_read(phydev, RTL821x_PAGE_SELECT); ++ if (oldpage < 0) ++ return oldpage; ++ ++ if (oldpage != page) { ++ ret = __phy_write(phydev, RTL821x_PAGE_SELECT, page); ++ if (ret < 0) ++ return ret; ++ } ++ ++ write_ret = __phy_write(phydev, RTL822X_VND2_TO_PAGE_REG(reg), val); ++ if (oldpage != page) { ++ ret = __phy_write(phydev, RTL821x_PAGE_SELECT, oldpage); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return write_ret; ++} ++ + static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1) + { + bool has_2500, has_sgmii; +@@ -2150,6 +2222,8 @@ static struct phy_driver realtek_drvs[] + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, ++ .read_mmd = rtl822xb_read_mmd, ++ .write_mmd = rtl822xb_write_mmd, + }, { + .match_phy_device = rtl8221b_match_phy_device, + .name = "RTL8226B_RTL8221B 2.5Gbps PHY", +@@ -2164,6 +2238,8 @@ static struct phy_driver realtek_drvs[] + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, ++ .read_mmd = rtl822xb_read_mmd, ++ .write_mmd = rtl822xb_write_mmd, + }, { + PHY_ID_MATCH_EXACT(0x001cc838), + .name = "RTL8226-CG 2.5Gbps PHY", +@@ -2176,6 +2252,8 @@ static struct phy_driver realtek_drvs[] + .read_status = rtl822xb_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, ++ .read_mmd = rtl822xb_read_mmd, ++ .write_mmd = rtl822xb_write_mmd, + }, { + PHY_ID_MATCH_EXACT(0x001cc848), + .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", +@@ -2190,6 +2268,8 @@ static struct phy_driver realtek_drvs[] + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, ++ .read_mmd = rtl822xb_read_mmd, ++ .write_mmd = rtl822xb_write_mmd, + }, { + .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, + .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", +@@ -2205,6 +2285,8 @@ static struct phy_driver realtek_drvs[] + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, ++ .read_mmd = rtl822xb_read_mmd, ++ .write_mmd = rtl822xb_write_mmd, + }, { + .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, + .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", +@@ -2235,6 +2317,8 @@ static struct phy_driver realtek_drvs[] + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, ++ .read_mmd = rtl822xb_read_mmd, ++ .write_mmd = rtl822xb_write_mmd, + }, { + .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device, + .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)", diff --git a/target/linux/generic/backport-6.12/791-v7.0-net-phy-realtek-get-rid-of-magic-number-in-rtlgen_re.patch b/target/linux/generic/backport-6.12/791-v7.0-net-phy-realtek-get-rid-of-magic-number-in-rtlgen_re.patch new file mode 100644 index 00000000000..992e6600391 --- /dev/null +++ b/target/linux/generic/backport-6.12/791-v7.0-net-phy-realtek-get-rid-of-magic-number-in-rtlgen_re.patch @@ -0,0 +1,30 @@ +From d8489935f5979b72e73de585037fe6fde7088bc3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 5 Jan 2026 16:39:26 +0000 +Subject: [PATCH 5/5] net: phy: realtek: get rid of magic number in + rtlgen_read_status() + +Use newly introduced helper macros RTL822X_VND2_TO_PAGE and +RTL822X_VND2_TO_PAGE_REG to access RTL_VEND2_PHYSR register over Clause-22 +paged access instead of using magic numbers. + +Signed-off-by: Daniel Golle +Reviewed-by: Maxime Chevallier +Link: https://patch.msgid.link/a53d4577335fdda4d363db9bc4bf614fd3a56c9b.1767630451.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/realtek/realtek_main.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1153,7 +1153,8 @@ static int rtlgen_read_status(struct phy + if (!phydev->link) + return 0; + +- val = phy_read_paged(phydev, 0xa43, 0x12); ++ val = phy_read_paged(phydev, RTL822X_VND2_TO_PAGE(RTL_VND2_PHYSR), ++ RTL822X_VND2_TO_PAGE_REG(RTL_VND2_PHYSR)); + if (val < 0) + return val; + diff --git a/target/linux/generic/backport-6.12/792-v7.0-net-phy-realtek-add-dummy-PHY-driver-for-RTL8127ATF.patch b/target/linux/generic/backport-6.12/792-v7.0-net-phy-realtek-add-dummy-PHY-driver-for-RTL8127ATF.patch new file mode 100644 index 00000000000..85b41114f11 --- /dev/null +++ b/target/linux/generic/backport-6.12/792-v7.0-net-phy-realtek-add-dummy-PHY-driver-for-RTL8127ATF.patch @@ -0,0 +1,123 @@ +From c4277d21ab694c7964a48759a5452e5bbbe12965 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Sat, 10 Jan 2026 16:14:05 +0100 +Subject: [PATCH] net: phy: realtek: add dummy PHY driver for RTL8127ATF + +RTL8127ATF supports a SFP+ port for fiber modules (10GBASE-SR/LR/ER/ZR and +DAC). The list of supported modes was provided by Realtek. According to the +r8127 vendor driver also 1G modules are supported, but this needs some more +complexity in the driver, and only 10G mode has been tested so far. +Therefore mainline support will be limited to 10G for now. +The SFP port signals are hidden in the chip IP and driven by firmware. +Therefore mainline SFP support can't be used here. +This PHY driver is used by the RTL8127ATF support in r8169. +RTL8127ATF reports the same PHY ID as the TP version. Therefore use a dummy +PHY ID. This PHY driver is used by the RTL8127ATF support in r8169. + +Signed-off-by: Heiner Kallweit +Link: https://patch.msgid.link/e3d55162-210a-4fab-9abf-99c6954eee10@gmail.com +Signed-off-by: Jakub Kicinski +--- + MAINTAINERS | 1 + + drivers/net/phy/realtek/realtek_main.c | 54 ++++++++++++++++++++++++++ + include/net/phy/realtek_phy.h | 7 ++++ + 3 files changed, 62 insertions(+) + create mode 100644 include/net/phy/realtek_phy.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -8459,6 +8459,7 @@ F: include/linux/phy_link_topology.h + F: include/linux/phylib_stubs.h + F: include/linux/platform_data/mdio-bcm-unimac.h + F: include/linux/platform_data/mdio-gpio.h ++F: include/net/phy/ + F: include/trace/events/mdio.h + F: include/uapi/linux/mdio.h + F: include/uapi/linux/mii.h +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + #include "realtek.h" + +@@ -2099,6 +2100,45 @@ static irqreturn_t rtl8221b_handle_inter + return IRQ_HANDLED; + } + ++static int rtlgen_sfp_get_features(struct phy_device *phydev) ++{ ++ linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, ++ phydev->supported); ++ ++ /* set default mode */ ++ phydev->speed = SPEED_10000; ++ phydev->duplex = DUPLEX_FULL; ++ ++ phydev->port = PORT_FIBRE; ++ ++ return 0; ++} ++ ++static int rtlgen_sfp_read_status(struct phy_device *phydev) ++{ ++ int val, err; ++ ++ err = genphy_update_link(phydev); ++ if (err) ++ return err; ++ ++ if (!phydev->link) ++ return 0; ++ ++ val = rtlgen_read_vend2(phydev, RTL_VND2_PHYSR); ++ if (val < 0) ++ return val; ++ ++ rtlgen_decode_physr(phydev, val); ++ ++ return 0; ++} ++ ++static int rtlgen_sfp_config_aneg(struct phy_device *phydev) ++{ ++ return 0; ++} ++ + static struct phy_driver realtek_drvs[] = { + { + PHY_ID_MATCH_EXACT(0x00008201), +@@ -2357,6 +2397,20 @@ static struct phy_driver realtek_drvs[] + .suspend = genphy_suspend, + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ .read_mmd = rtl822x_read_mmd, ++ .write_mmd = rtl822x_write_mmd, ++ }, { ++ PHY_ID_MATCH_EXACT(PHY_ID_RTL_DUMMY_SFP), ++ .name = "Realtek SFP PHY Mode", ++ .flags = PHY_IS_INTERNAL, ++ .probe = rtl822x_probe, ++ .get_features = rtlgen_sfp_get_features, ++ .config_aneg = rtlgen_sfp_config_aneg, ++ .read_status = rtlgen_sfp_read_status, ++ .suspend = genphy_suspend, ++ .resume = rtlgen_resume, ++ .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + .read_mmd = rtl822x_read_mmd, + .write_mmd = rtl822x_write_mmd, +--- /dev/null ++++ b/include/net/phy/realtek_phy.h +@@ -0,0 +1,7 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef _REALTEK_PHY_H ++#define _REALTEK_PHY_H ++ ++#define PHY_ID_RTL_DUMMY_SFP 0x001ccbff ++ ++#endif /* _REALTEK_PHY_H */ diff --git a/target/linux/generic/backport-6.12/793-v7.0-net-phy-realtek-fix-in-band-capabilities-for-2.5G-PH.patch b/target/linux/generic/backport-6.12/793-v7.0-net-phy-realtek-fix-in-band-capabilities-for-2.5G-PH.patch new file mode 100644 index 00000000000..7e44ea9b48c --- /dev/null +++ b/target/linux/generic/backport-6.12/793-v7.0-net-phy-realtek-fix-in-band-capabilities-for-2.5G-PH.patch @@ -0,0 +1,35 @@ +From 8744b63e8a9ac4a3c30b557ca6bc115851a980e9 Mon Sep 17 00:00:00 2001 +From: Jan Hoffmann +Date: Tue, 13 Jan 2026 21:55:44 +0100 +Subject: [PATCH] net: phy: realtek: fix in-band capabilities for 2.5G PHYs + +It looks like the configuration of in-band AN only affects SGMII, and it +is always disabled for 2500Base-X. Adjust the reported capabilities +accordingly. + +This is based on testing using OpenWrt on Zyxel XGS1010-12 rev A1 with +RTL8226-CG, and Zyxel XGS1210-12 rev B1 with RTL8221B-VB-CG. On these +devices, 2500Base-X in-band AN is known to work with some SFP modules +(containing an unknown PHY). However, with the built-in Realtek PHYs, +no auto-negotiation takes place, irrespective of the configuration of +the PHY. + +Fixes: 10fbd71fc5f9b ("net: phy: realtek: implement configuring in-band an") +Signed-off-by: Jan Hoffmann +Reviewed-by: Daniel Golle +Link: https://patch.msgid.link/20260113205557.503409-1-jan@3e8.eu +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1428,6 +1428,7 @@ static unsigned int rtl822x_inband_caps( + { + switch (interface) { + case PHY_INTERFACE_MODE_2500BASEX: ++ return LINK_INBAND_DISABLE; + case PHY_INTERFACE_MODE_SGMII: + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + default: diff --git a/target/linux/generic/backport-6.12/794-v7.0-net-phy-realtek-support-interrupt-also-for-C22-varia.patch b/target/linux/generic/backport-6.12/794-v7.0-net-phy-realtek-support-interrupt-also-for-C22-varia.patch new file mode 100644 index 00000000000..ea3347d3bb0 --- /dev/null +++ b/target/linux/generic/backport-6.12/794-v7.0-net-phy-realtek-support-interrupt-also-for-C22-varia.patch @@ -0,0 +1,38 @@ +From 84fb8b93fae2a4c53323b2bf6d81e7ddcc8e7cf4 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 13 Jan 2026 03:44:00 +0000 +Subject: [PATCH 1/5] net: phy: realtek: support interrupt also for C22 + variants + +Now that access to MDIO_MMD_VEND2 works transparently also in Clause-22 +mode, add interrupt support also for the C22 variants of the +RTL8221B-VB-CG and RTL8221B-VM-CG. This results in the C22 and C45 +driver instances now having all the same features implemented. + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/7620084b1de01580edc2d0e1b9548507fb4643a8.1768275364.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -2315,6 +2315,8 @@ static struct phy_driver realtek_drvs[] + }, { + .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, + .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", ++ .config_intr = rtl8221b_config_intr, ++ .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, +@@ -2347,6 +2349,8 @@ static struct phy_driver realtek_drvs[] + }, { + .match_phy_device = rtl8221b_vm_cg_c22_match_phy_device, + .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", ++ .config_intr = rtl8221b_config_intr, ++ .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, diff --git a/target/linux/generic/backport-6.12/795-v7.0-net-phy-realtek-simplify-C22-reg-access-via-MDIO_MMD.patch b/target/linux/generic/backport-6.12/795-v7.0-net-phy-realtek-simplify-C22-reg-access-via-MDIO_MMD.patch new file mode 100644 index 00000000000..a67b0ef7c4b --- /dev/null +++ b/target/linux/generic/backport-6.12/795-v7.0-net-phy-realtek-simplify-C22-reg-access-via-MDIO_MMD.patch @@ -0,0 +1,61 @@ +From 2809a1c4340437f2ff6b73c0094d7ca51b575e1b Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 13 Jan 2026 03:44:17 +0000 +Subject: [PATCH 2/5] net: phy: realtek: simplify C22 reg access via + MDIO_MMD_VEND2 + +RealTek 2.5GE PHYs have all standard Clause-22 registers mapped also +inside MDIO_MMD_VEND2 at offset 0xa400. This is used mainly in case the +PHY is connected to a Clause-45-only bus. The RTL8221B is frequently +used in copper SFP module which uses the RollBall MDIO-over-I2C +method which *only* supports Clause-45, for example. + +In order to support using the PHY on Clause-45-only busses, the PHY +driver has previously been split into a C22-only and C45-only instances, +creating quite a bit of redundancy and confusion. + +In preparation of reunifying the two driver instances, add support for +translating MDIO_MMD_VEND2 registers 0xa400 to 0xa43c back to Clause-22 +registers 0 to 30 in case the PHY is accessed on a Clause-22 bus. + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/fd49d86bd0445b76269fd3ea456c709c2066683f.1768275364.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -143,6 +143,7 @@ + + #define RTL822X_VND2_TO_PAGE(reg) ((reg) >> 4) + #define RTL822X_VND2_TO_PAGE_REG(reg) (16 + (((reg) & GENMASK(3, 0)) >> 1)) ++#define RTL822X_VND2_TO_C22_REG(reg) (((reg) - 0xa400) / 2) + #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg)) + + #define RTL8221B_VND2_INER 0xa4d2 +@@ -1264,6 +1265,11 @@ static int rtl822xb_read_mmd(struct phy_ + return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr, + phydev->is_c45, devnum, reg); + ++ /* Simplify access to C22-registers addressed inside MDIO_MMD_VEND2 */ ++ if (reg >= RTL822X_VND2_C22_REG(0) && ++ reg <= RTL822X_VND2_C22_REG(30)) ++ return __phy_read(phydev, RTL822X_VND2_TO_C22_REG(reg)); ++ + /* Use paged access for MDIO_MMD_VEND2 over Clause-22 */ + page = RTL822X_VND2_TO_PAGE(reg); + oldpage = __phy_read(phydev, RTL821x_PAGE_SELECT); +@@ -1299,6 +1305,11 @@ static int rtl822xb_write_mmd(struct phy + return mmd_phy_write(phydev->mdio.bus, phydev->mdio.addr, + phydev->is_c45, devnum, reg, val); + ++ /* Simplify access to C22-registers addressed inside MDIO_MMD_VEND2 */ ++ if (reg >= RTL822X_VND2_C22_REG(0) && ++ reg <= RTL822X_VND2_C22_REG(30)) ++ return __phy_write(phydev, RTL822X_VND2_TO_C22_REG(reg), val); ++ + /* Use paged access for MDIO_MMD_VEND2 over Clause-22 */ + page = RTL822X_VND2_TO_PAGE(reg); + oldpage = __phy_read(phydev, RTL821x_PAGE_SELECT); diff --git a/target/linux/generic/backport-6.12/796-v7.0-net-phy-realtek-reunify-C22-and-C45-drivers.patch b/target/linux/generic/backport-6.12/796-v7.0-net-phy-realtek-reunify-C22-and-C45-drivers.patch new file mode 100644 index 00000000000..5bca3035d97 --- /dev/null +++ b/target/linux/generic/backport-6.12/796-v7.0-net-phy-realtek-reunify-C22-and-C45-drivers.patch @@ -0,0 +1,133 @@ +From 85f75da86a0adc4798d3675aab3365e721a1dbf5 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 13 Jan 2026 03:44:25 +0000 +Subject: [PATCH 3/5] net: phy: realtek: reunify C22 and C45 drivers + +Reunify the split C22/C45 drivers for the RTL8221B-VB-CG 2.5Gbps and +RTL8221B-VM-CG 2.5Gbps PHYs back into a single driver. + +This is possible now by using all the driver operations previously used +by the C45 driver, as transparent access to all MMDs including +MDIO_MMD_VEND2 is now possible also over Clause-22 MDIO. + +The unified driver will still only use Clause-45 access on any Clause-45 +capable busses while still working fine on Clause-22 busses. + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/bffcb85fdc20e07056976962d3caaa1be5d0ddb0.1768275364.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 72 ++++++-------------------- + 1 file changed, 16 insertions(+), 56 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1880,28 +1880,18 @@ static int rtl8221b_match_phy_device(str + return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev); + } + +-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev, +- const struct phy_driver *phydrv) ++static int rtl8221b_vb_cg_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { +- return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); ++ return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true) || ++ rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); + } + +-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev, +- const struct phy_driver *phydrv) ++static int rtl8221b_vm_cg_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { +- return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true); +-} +- +-static int rtl8221b_vm_cg_c22_match_phy_device(struct phy_device *phydev, +- const struct phy_driver *phydrv) +-{ +- return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, false); +-} +- +-static int rtl8221b_vm_cg_c45_match_phy_device(struct phy_device *phydev, +- const struct phy_driver *phydrv) +-{ +- return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, true); ++ return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, true) || ++ rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, false); + } + + static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev, +@@ -2324,27 +2314,8 @@ static struct phy_driver realtek_drvs[] + .read_mmd = rtl822xb_read_mmd, + .write_mmd = rtl822xb_write_mmd, + }, { +- .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, +- .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", +- .config_intr = rtl8221b_config_intr, +- .handle_interrupt = rtl8221b_handle_interrupt, +- .probe = rtl822x_probe, +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .config_init = rtl822xb_config_init, +- .inband_caps = rtl822x_inband_caps, +- .config_inband = rtl822x_config_inband, +- .get_rate_matching = rtl822xb_get_rate_matching, +- .read_status = rtl822xb_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, +- .read_page = rtl821x_read_page, +- .write_page = rtl821x_write_page, +- .read_mmd = rtl822xb_read_mmd, +- .write_mmd = rtl822xb_write_mmd, +- }, { +- .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, +- .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", ++ .match_phy_device = rtl8221b_vb_cg_match_phy_device, ++ .name = "RTL8221B-VB-CG 2.5Gbps PHY", + .config_intr = rtl8221b_config_intr, + .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, +@@ -2357,28 +2328,13 @@ static struct phy_driver realtek_drvs[] + .read_status = rtl822xb_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, +- }, { +- .match_phy_device = rtl8221b_vm_cg_c22_match_phy_device, +- .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", +- .config_intr = rtl8221b_config_intr, +- .handle_interrupt = rtl8221b_handle_interrupt, +- .probe = rtl822x_probe, +- .get_features = rtl822x_get_features, +- .config_aneg = rtl822x_config_aneg, +- .config_init = rtl822xb_config_init, +- .inband_caps = rtl822x_inband_caps, +- .config_inband = rtl822x_config_inband, +- .get_rate_matching = rtl822xb_get_rate_matching, +- .read_status = rtl822xb_read_status, +- .suspend = genphy_suspend, +- .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + .read_mmd = rtl822xb_read_mmd, + .write_mmd = rtl822xb_write_mmd, + }, { +- .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device, +- .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)", ++ .match_phy_device = rtl8221b_vm_cg_match_phy_device, ++ .name = "RTL8221B-VM-CG 2.5Gbps PHY", + .config_intr = rtl8221b_config_intr, + .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, +@@ -2391,6 +2347,10 @@ static struct phy_driver realtek_drvs[] + .read_status = rtl822xb_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, ++ .read_page = rtl821x_read_page, ++ .write_page = rtl821x_write_page, ++ .read_mmd = rtl822xb_read_mmd, ++ .write_mmd = rtl822xb_write_mmd, + }, { + .match_phy_device = rtl8251b_c45_match_phy_device, + .name = "RTL8251B 5Gbps PHY", diff --git a/target/linux/generic/backport-6.12/797-v7.0-net-phy-realtek-demystify-PHYSR-register-location.patch b/target/linux/generic/backport-6.12/797-v7.0-net-phy-realtek-demystify-PHYSR-register-location.patch new file mode 100644 index 00000000000..185e0fdf729 --- /dev/null +++ b/target/linux/generic/backport-6.12/797-v7.0-net-phy-realtek-demystify-PHYSR-register-location.patch @@ -0,0 +1,90 @@ +From 46ff862d376cfadf0f9e36a6edce41a003175708 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 13 Jan 2026 03:44:33 +0000 +Subject: [PATCH 4/5] net: phy: realtek: demystify PHYSR register location + +Turns out that register address RTL_VND2_PHYSR (0xa434) maps to +Clause-22 register MII_RESV2. Use that to get rid of yet another magic +number, and rename access macros accordingly. + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/6ed246e0aa3ca8038d2fa432d51518959fb89b6b.1768275364.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -178,12 +178,12 @@ + #define RTL9000A_GINMR 0x14 + #define RTL9000A_GINMR_LINK_STATUS BIT(4) + +-#define RTL_VND2_PHYSR 0xa434 +-#define RTL_VND2_PHYSR_DUPLEX BIT(3) +-#define RTL_VND2_PHYSR_SPEEDL GENMASK(5, 4) +-#define RTL_VND2_PHYSR_SPEEDH GENMASK(10, 9) +-#define RTL_VND2_PHYSR_MASTER BIT(11) +-#define RTL_VND2_PHYSR_SPEED_MASK (RTL_VND2_PHYSR_SPEEDL | RTL_VND2_PHYSR_SPEEDH) ++#define RTL_PHYSR MII_RESV2 ++#define RTL_PHYSR_DUPLEX BIT(3) ++#define RTL_PHYSR_SPEEDL GENMASK(5, 4) ++#define RTL_PHYSR_SPEEDH GENMASK(10, 9) ++#define RTL_PHYSR_MASTER BIT(11) ++#define RTL_PHYSR_SPEED_MASK (RTL_PHYSR_SPEEDL | RTL_PHYSR_SPEEDH) + + #define RTL_MDIO_PCS_EEE_ABLE 0xa5c4 + #define RTL_MDIO_AN_EEE_ADV 0xa5d0 +@@ -1102,12 +1102,12 @@ static void rtlgen_decode_physr(struct p + * 0: Half Duplex + * 1: Full Duplex + */ +- if (val & RTL_VND2_PHYSR_DUPLEX) ++ if (val & RTL_PHYSR_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + +- switch (val & RTL_VND2_PHYSR_SPEED_MASK) { ++ switch (val & RTL_PHYSR_SPEED_MASK) { + case 0x0000: + phydev->speed = SPEED_10; + break; +@@ -1135,7 +1135,7 @@ static void rtlgen_decode_physr(struct p + * 1: Master Mode + */ + if (phydev->speed >= 1000) { +- if (val & RTL_VND2_PHYSR_MASTER) ++ if (val & RTL_PHYSR_MASTER) + phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER; + else + phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE; +@@ -1155,8 +1155,7 @@ static int rtlgen_read_status(struct phy + if (!phydev->link) + return 0; + +- val = phy_read_paged(phydev, RTL822X_VND2_TO_PAGE(RTL_VND2_PHYSR), +- RTL822X_VND2_TO_PAGE_REG(RTL_VND2_PHYSR)); ++ val = phy_read(phydev, RTL_PHYSR); + if (val < 0) + return val; + +@@ -1623,7 +1622,8 @@ static int rtl822x_c45_read_status(struc + } + + /* Read actual speed from vendor register. */ +- val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_VND2_PHYSR); ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ RTL822X_VND2_C22_REG(RTL_PHYSR)); + if (val < 0) + return val; + +@@ -2127,7 +2127,7 @@ static int rtlgen_sfp_read_status(struct + if (!phydev->link) + return 0; + +- val = rtlgen_read_vend2(phydev, RTL_VND2_PHYSR); ++ val = phy_read(phydev, RTL_PHYSR); + if (val < 0) + return val; + diff --git a/target/linux/generic/backport-6.12/798-v7.0-net-phy-realtek-simplify-bogus-paged-operations.patch b/target/linux/generic/backport-6.12/798-v7.0-net-phy-realtek-simplify-bogus-paged-operations.patch new file mode 100644 index 00000000000..f03ad662c5b --- /dev/null +++ b/target/linux/generic/backport-6.12/798-v7.0-net-phy-realtek-simplify-bogus-paged-operations.patch @@ -0,0 +1,93 @@ +From 650e55f224a575cdb18c984b95036109519502d1 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 13 Jan 2026 03:44:42 +0000 +Subject: [PATCH 5/5] net: phy: realtek: simplify bogus paged operations + +Only registers 0x10~0x17 are affected by the value in the page +selection register 0x1f. Hence there is no point in using paged +operations when accessing any other registers. +Simplify the driver by using the normal phy_read and phy_write +operations for registers which are anyway not affected by paging. + +Signed-off-by: Daniel Golle +Link: https://patch.msgid.link/0c5cbb66ce3e72a011d76f8c3d61ebcac44483bb.1768275364.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/realtek/realtek_main.c | 19 ++++++++----------- + 1 file changed, 8 insertions(+), 11 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -67,7 +67,6 @@ + #define RTL8211E_DELAY_MASK GENMASK(13, 11) + + /* RTL8211F PHY configuration */ +-#define RTL8211F_PHYCR_PAGE 0xa43 + #define RTL8211F_PHYCR1 0x18 + #define RTL8211F_ALDPS_PLL_OFF BIT(1) + #define RTL8211F_ALDPS_ENABLE BIT(2) +@@ -77,7 +76,6 @@ + #define RTL8211F_CLKOUT_EN BIT(0) + #define RTL8211F_PHYCR2_PHY_EEE_ENABLE BIT(5) + +-#define RTL8211F_INSR_PAGE 0xa43 + #define RTL8211F_INSR 0x1d + + /* RTL8211F LED configuration */ +@@ -332,7 +330,7 @@ static int rtl8211f_ack_interrupt(struct + { + int err; + +- err = phy_read_paged(phydev, RTL8211F_INSR_PAGE, RTL8211F_INSR); ++ err = phy_read(phydev, RTL8211F_INSR); + + return (err < 0) ? err : 0; + } +@@ -478,7 +476,7 @@ static irqreturn_t rtl8211f_handle_inter + { + int irq_status; + +- irq_status = phy_read_paged(phydev, RTL8211F_INSR_PAGE, RTL8211F_INSR); ++ irq_status = phy_read(phydev, RTL8211F_INSR); + if (irq_status < 0) { + phy_error(phydev); + return IRQ_NONE; +@@ -669,8 +667,8 @@ static int rtl8211f_config_clk_out(struc + RTL8211FVD_CLKOUT_REG, + RTL8211FVD_CLKOUT_EN, 0); + else +- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, +- RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, 0); ++ ret = phy_modify(phydev, RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, ++ 0); + if (ret) + return ret; + +@@ -695,15 +693,14 @@ static int rtl8211f_config_aldps(struct + if (!priv->enable_aldps) + return 0; + +- return phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1, +- mask, mask); ++ return phy_modify(phydev, RTL8211F_PHYCR1, mask, mask); + } + + static int rtl8211f_config_phy_eee(struct phy_device *phydev) + { + /* Disable PHY-mode EEE so LPI is passed to the MAC */ +- return phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2, +- RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0); ++ return phy_modify(phydev, RTL8211F_PHYCR2, ++ RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0); + } + + static int rtl8211f_config_init(struct phy_device *phydev) +@@ -769,7 +766,7 @@ static int rtl8211f_suspend(struct phy_d + goto err; + + /* Read the INSR to clear any pending interrupt */ +- phy_read_paged(phydev, RTL8211F_INSR_PAGE, RTL8211F_INSR); ++ phy_read(phydev, RTL8211F_INSR); + + /* Reset the WoL to ensure that an event is picked up. + * Unless we do this, even if we receive another packet, diff --git a/target/linux/generic/backport-6.12/799-v7.0-mtd-physmap-core-Prioritize-ofparts-for-OF-probe.patch b/target/linux/generic/backport-6.12/799-v7.0-mtd-physmap-core-Prioritize-ofparts-for-OF-probe.patch new file mode 100644 index 00000000000..4aac6430f2d --- /dev/null +++ b/target/linux/generic/backport-6.12/799-v7.0-mtd-physmap-core-Prioritize-ofparts-for-OF-probe.patch @@ -0,0 +1,33 @@ +From cd3303e9081050ad09b031bc5b58c89fe6902f86 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Thu, 29 Jan 2026 00:48:36 +0100 +Subject: [PATCH] mtd: physmap-core: Prioritize ofparts for OF probe + +Place the ofparts before RedBoot partitions in the OF probe +path: this makes it possible to override any existing +RedBoot partitions with fixed-partitions which may be necessary, +such as when you need to repartition the flash from Linux' +point of view but not necessary from the bootloaders point +of view. + +This happens when a device such as Raidsonic IB-4220-B has +three partitions named "Kern", "Ramdisk" and "Application" +that we want to merge into one for more efficient use +of the flash memory in OpenWrt. + +Signed-off-by: Linus Walleij +--- + drivers/mtd/maps/physmap-core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mtd/maps/physmap-core.c ++++ b/drivers/mtd/maps/physmap-core.c +@@ -268,7 +268,7 @@ static const struct of_device_id of_flas + MODULE_DEVICE_TABLE(of, of_flash_match); + + static const char * const of_default_part_probes[] = { +- "cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL ++ "cmdlinepart", "ofpart", "ofoldpart", "RedBoot", NULL + }; + + static const char * const *of_get_part_probes(struct platform_device *dev) diff --git a/target/linux/generic/backport-6.12/800-v6.13-hwmon-Add-static-visibility-member-to-struct-hwmon_o.patch b/target/linux/generic/backport-6.12/800-v6.13-hwmon-Add-static-visibility-member-to-struct-hwmon_o.patch new file mode 100644 index 00000000000..0c9f920562d --- /dev/null +++ b/target/linux/generic/backport-6.12/800-v6.13-hwmon-Add-static-visibility-member-to-struct-hwmon_o.patch @@ -0,0 +1,88 @@ +From 79bc0af904db647979c735563299c9b0d820e432 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Thu, 10 Oct 2024 21:35:42 +0200 +Subject: [PATCH] hwmon: Add static visibility member to struct hwmon_ops + +Several drivers return the same static value in their is_visible +callback, what results in code duplication. Therefore add an option +for drivers to specify a static visibility directly. + +Signed-off-by: Heiner Kallweit +Message-ID: <89690b81-2c73-47ae-9ae9-45c77b45ca0c@gmail.com> +groeck: Renamed hwmon_ops_is_visible -> hwmon_is_visible +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/hwmon.c | 19 +++++++++++++++---- + include/linux/hwmon.h | 5 ++++- + 2 files changed, 19 insertions(+), 5 deletions(-) + +--- a/drivers/hwmon/hwmon.c ++++ b/drivers/hwmon/hwmon.c +@@ -145,6 +145,17 @@ static const struct class hwmon_class = + + static DEFINE_IDA(hwmon_ida); + ++static umode_t hwmon_is_visible(const struct hwmon_ops *ops, ++ const void *drvdata, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ if (ops->visible) ++ return ops->visible; ++ ++ return ops->is_visible(drvdata, type, attr, channel); ++} ++ + /* Thermal zone handling */ + + /* +@@ -267,8 +278,8 @@ static int hwmon_thermal_register_sensor + int err; + + if (!(info[i]->config[j] & HWMON_T_INPUT) || +- !chip->ops->is_visible(drvdata, hwmon_temp, +- hwmon_temp_input, j)) ++ !hwmon_is_visible(chip->ops, drvdata, hwmon_temp, ++ hwmon_temp_input, j)) + continue; + + err = hwmon_thermal_add_sensor(dev, j); +@@ -506,7 +517,7 @@ static struct attribute *hwmon_genattr(c + const char *name; + bool is_string = is_string_attr(type, attr); + +- mode = ops->is_visible(drvdata, type, attr, index); ++ mode = hwmon_is_visible(ops, drvdata, type, attr, index); + if (!mode) + return ERR_PTR(-ENOENT); + +@@ -1033,7 +1044,7 @@ hwmon_device_register_with_info(struct d + if (!dev || !name || !chip) + return ERR_PTR(-EINVAL); + +- if (!chip->ops || !chip->ops->is_visible || !chip->info) ++ if (!chip->ops || !(chip->ops->visible || chip->ops->is_visible) || !chip->info) + return ERR_PTR(-EINVAL); + + return __hwmon_device_register(dev, name, drvdata, chip, extra_groups); +--- a/include/linux/hwmon.h ++++ b/include/linux/hwmon.h +@@ -368,7 +368,9 @@ enum hwmon_intrusion_attributes { + + /** + * struct hwmon_ops - hwmon device operations +- * @is_visible: Callback to return attribute visibility. Mandatory. ++ * @visible: Static visibility. If non-zero, 'is_visible' is ignored. ++ * @is_visible: Callback to return attribute visibility. Mandatory unless ++ * 'visible' is non-zero. + * Parameters are: + * @const void *drvdata: + * Pointer to driver-private data structure passed +@@ -412,6 +414,7 @@ enum hwmon_intrusion_attributes { + * The function returns 0 on success or a negative error number. + */ + struct hwmon_ops { ++ umode_t visible; + umode_t (*is_visible)(const void *drvdata, enum hwmon_sensor_types type, + u32 attr, int channel); + int (*read)(struct device *dev, enum hwmon_sensor_types type, diff --git a/target/linux/generic/backport-6.12/801-01-v6.13-dt-bindings-clocks-add-binding-for-gated-fixed-clock.patch b/target/linux/generic/backport-6.12/801-01-v6.13-dt-bindings-clocks-add-binding-for-gated-fixed-clock.patch new file mode 100644 index 00000000000..f5ab8a0e3ba --- /dev/null +++ b/target/linux/generic/backport-6.12/801-01-v6.13-dt-bindings-clocks-add-binding-for-gated-fixed-clock.patch @@ -0,0 +1,82 @@ +From a4a7cbe36623ffaabbae413c0eacf40d033db71f Mon Sep 17 00:00:00 2001 +From: Heiko Stuebner +Date: Fri, 6 Sep 2024 10:25:07 +0200 +Subject: dt-bindings: clocks: add binding for gated-fixed-clocks + +In contrast to fixed clocks that are described as ungateable, boards +sometimes use additional oscillators for things like PCIe reference +clocks, that need actual supplies to get enabled and enable-gpios to be +toggled for them to work. + +This adds a binding for such oscillators that are not configurable +themself, but need to handle supplies for them to work. + +In schematics they often can be seen as + + ---------------- +Enable - | 100MHz,3.3V, | - VDD + | 3225 | + GND - | | - OUT + ---------------- + +or similar. The enable pin might be separate but can also just be tied +to the vdd supply, hence it is optional in the binding. + +Signed-off-by: Heiko Stuebner +Reviewed-by: Rob Herring (Arm) +Reviewed-by: Conor Dooley +Link: https://lore.kernel.org/r/20240906082511.2963890-2-heiko@sntech.de +Signed-off-by: Stephen Boyd + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/gated-fixed-clock.yaml +@@ -0,0 +1,49 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/clock/gated-fixed-clock.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Gated Fixed clock ++ ++maintainers: ++ - Heiko Stuebner ++ ++properties: ++ compatible: ++ const: gated-fixed-clock ++ ++ "#clock-cells": ++ const: 0 ++ ++ clock-frequency: true ++ ++ clock-output-names: ++ maxItems: 1 ++ ++ enable-gpios: ++ description: ++ Contains a single GPIO specifier for the GPIO that enables and disables ++ the oscillator. ++ maxItems: 1 ++ ++ vdd-supply: ++ description: handle of the regulator that provides the supply voltage ++ ++required: ++ - compatible ++ - "#clock-cells" ++ - clock-frequency ++ - vdd-supply ++ ++additionalProperties: false ++ ++examples: ++ - | ++ clock-1000000000 { ++ compatible = "gated-fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <1000000000>; ++ vdd-supply = <®_vdd>; ++ }; ++... diff --git a/target/linux/generic/backport-6.12/801-02-v6.13-clk-clk-gpio-update-documentation-for-gpio-gate-cloc.patch b/target/linux/generic/backport-6.12/801-02-v6.13-clk-clk-gpio-update-documentation-for-gpio-gate-cloc.patch new file mode 100644 index 00000000000..99e41a1e990 --- /dev/null +++ b/target/linux/generic/backport-6.12/801-02-v6.13-clk-clk-gpio-update-documentation-for-gpio-gate-cloc.patch @@ -0,0 +1,27 @@ +From 6cb137c7e99f8307f1f0fcccb1896f2d3b0651d3 Mon Sep 17 00:00:00 2001 +From: Heiko Stuebner +Date: Fri, 6 Sep 2024 10:25:08 +0200 +Subject: clk: clk-gpio: update documentation for gpio-gate clock + +The main documentation block seems to be from a time before the driver +handled sleeping and non-sleeping gpios and with that change it seems +updating the doc was overlooked. So do that now. + +Signed-off-by: Heiko Stuebner +Link: https://lore.kernel.org/r/20240906082511.2963890-3-heiko@sntech.de +Signed-off-by: Stephen Boyd + +--- a/drivers/clk/clk-gpio.c ++++ b/drivers/clk/clk-gpio.c +@@ -22,8 +22,9 @@ + * DOC: basic gpio gated clock which can be enabled and disabled + * with gpio output + * Traits of this clock: +- * prepare - clk_(un)prepare only ensures parent is (un)prepared +- * enable - clk_enable and clk_disable are functional & control gpio ++ * prepare - clk_(un)prepare are functional and control a gpio that can sleep ++ * enable - clk_enable and clk_disable are functional & control ++ * non-sleeping gpio + * rate - inherits rate from parent. No clk_set_rate support + * parent - fixed parent. No clk_set_parent support + */ diff --git a/target/linux/generic/backport-6.12/801-03-v6.13-clk-clk-gpio-use-dev_err_probe-for-gpio-get-failure.patch b/target/linux/generic/backport-6.12/801-03-v6.13-clk-clk-gpio-use-dev_err_probe-for-gpio-get-failure.patch new file mode 100644 index 00000000000..1982bb365e7 --- /dev/null +++ b/target/linux/generic/backport-6.12/801-03-v6.13-clk-clk-gpio-use-dev_err_probe-for-gpio-get-failure.patch @@ -0,0 +1,44 @@ +From 36abe81d9c3fa200a57ef2363e93a2991e387e19 Mon Sep 17 00:00:00 2001 +From: Heiko Stuebner +Date: Fri, 6 Sep 2024 10:25:09 +0200 +Subject: clk: clk-gpio: use dev_err_probe for gpio-get failure + +This is a real driver and dev_err_probe will hide the distinction between +EPROBE_DEFER and other errors automatically, so there is no need to +open-code this. + +Signed-off-by: Heiko Stuebner +Link: https://lore.kernel.org/r/20240906082511.2963890-4-heiko@sntech.de +Signed-off-by: Stephen Boyd + +--- a/drivers/clk/clk-gpio.c ++++ b/drivers/clk/clk-gpio.c +@@ -200,7 +200,6 @@ static int gpio_clk_driver_probe(struct + struct gpio_desc *gpiod; + struct clk_hw *hw; + bool is_mux; +- int ret; + + is_mux = of_device_is_compatible(node, "gpio-mux-clock"); + +@@ -212,17 +211,9 @@ static int gpio_clk_driver_probe(struct + + gpio_name = is_mux ? "select" : "enable"; + gpiod = devm_gpiod_get(dev, gpio_name, GPIOD_OUT_LOW); +- if (IS_ERR(gpiod)) { +- ret = PTR_ERR(gpiod); +- if (ret == -EPROBE_DEFER) +- pr_debug("%pOFn: %s: GPIOs not yet available, retry later\n", +- node, __func__); +- else +- pr_err("%pOFn: %s: Can't get '%s' named GPIO property\n", +- node, __func__, +- gpio_name); +- return ret; +- } ++ if (IS_ERR(gpiod)) ++ return dev_err_probe(dev, PTR_ERR(gpiod), ++ "Can't get '%s' named GPIO property\n", gpio_name); + + if (is_mux) + hw = clk_hw_register_gpio_mux(dev, gpiod); diff --git a/target/linux/generic/backport-6.12/801-04-v6.13-clk-clk-gpio-add-driver-for-gated-fixed-clocks.patch b/target/linux/generic/backport-6.12/801-04-v6.13-clk-clk-gpio-add-driver-for-gated-fixed-clocks.patch new file mode 100644 index 00000000000..44da746fdc6 --- /dev/null +++ b/target/linux/generic/backport-6.12/801-04-v6.13-clk-clk-gpio-add-driver-for-gated-fixed-clocks.patch @@ -0,0 +1,229 @@ +From 4940071d962827467f7297be3b874c96186ca0b7 Mon Sep 17 00:00:00 2001 +From: Heiko Stuebner +Date: Fri, 6 Sep 2024 10:25:10 +0200 +Subject: clk: clk-gpio: add driver for gated-fixed-clocks + +In contrast to fixed clocks that are described as ungateable, boards +sometimes use additional oscillators for things like PCIe reference +clocks, that need actual supplies to get enabled and enable-gpios to be +toggled for them to work. + +This adds a driver for those generic gated-fixed-clocks +that can show up in schematics looking like + + ---------------- +Enable - | 100MHz,3.3V, | - VDD + | 3225 | + GND - | | - OUT + ---------------- + +The new driver gets grouped together with the existing gpio-gate and +gpio-mux, as it for one re-uses a lot of the gpio-gate functions +and also in its core it's just another gpio-controlled clock, just +with a fixed rate and a regulator-supply added in. + +The regulator-API provides function stubs for the !CONFIG_REGULATOR case, +so no special handling is necessary. + +Signed-off-by: Heiko Stuebner +Link: https://lore.kernel.org/r/20240906082511.2963890-5-heiko@sntech.de +Signed-off-by: Stephen Boyd + +--- a/drivers/clk/clk-gpio.c ++++ b/drivers/clk/clk-gpio.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + /** + * DOC: basic gpio gated clock which can be enabled and disabled +@@ -239,3 +240,187 @@ static struct platform_driver gpio_clk_d + }, + }; + builtin_platform_driver(gpio_clk_driver); ++ ++/** ++ * DOC: gated fixed clock, controlled with a gpio output and a regulator ++ * Traits of this clock: ++ * prepare - clk_prepare and clk_unprepare are function & control regulator ++ * optionally a gpio that can sleep ++ * enable - clk_enable and clk_disable are functional & control gpio ++ * rate - rate is fixed and set on clock registration ++ * parent - fixed clock is a root clock and has no parent ++ */ ++ ++/** ++ * struct clk_gated_fixed - Gateable fixed rate clock ++ * @clk_gpio: instance of clk_gpio for gate-gpio ++ * @supply: supply regulator ++ * @rate: fixed rate ++ */ ++struct clk_gated_fixed { ++ struct clk_gpio clk_gpio; ++ struct regulator *supply; ++ unsigned long rate; ++}; ++ ++#define to_clk_gated_fixed(_clk_gpio) container_of(_clk_gpio, struct clk_gated_fixed, clk_gpio) ++ ++static unsigned long clk_gated_fixed_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ return to_clk_gated_fixed(to_clk_gpio(hw))->rate; ++} ++ ++static int clk_gated_fixed_prepare(struct clk_hw *hw) ++{ ++ struct clk_gated_fixed *clk = to_clk_gated_fixed(to_clk_gpio(hw)); ++ ++ if (!clk->supply) ++ return 0; ++ ++ return regulator_enable(clk->supply); ++} ++ ++static void clk_gated_fixed_unprepare(struct clk_hw *hw) ++{ ++ struct clk_gated_fixed *clk = to_clk_gated_fixed(to_clk_gpio(hw)); ++ ++ if (!clk->supply) ++ return; ++ ++ regulator_disable(clk->supply); ++} ++ ++static int clk_gated_fixed_is_prepared(struct clk_hw *hw) ++{ ++ struct clk_gated_fixed *clk = to_clk_gated_fixed(to_clk_gpio(hw)); ++ ++ if (!clk->supply) ++ return true; ++ ++ return regulator_is_enabled(clk->supply); ++} ++ ++/* ++ * Fixed gated clock with non-sleeping gpio. ++ * ++ * Prepare operation turns on the supply regulator ++ * and the enable operation switches the enable-gpio. ++ */ ++static const struct clk_ops clk_gated_fixed_ops = { ++ .prepare = clk_gated_fixed_prepare, ++ .unprepare = clk_gated_fixed_unprepare, ++ .is_prepared = clk_gated_fixed_is_prepared, ++ .enable = clk_gpio_gate_enable, ++ .disable = clk_gpio_gate_disable, ++ .is_enabled = clk_gpio_gate_is_enabled, ++ .recalc_rate = clk_gated_fixed_recalc_rate, ++}; ++ ++static int clk_sleeping_gated_fixed_prepare(struct clk_hw *hw) ++{ ++ int ret; ++ ++ ret = clk_gated_fixed_prepare(hw); ++ if (ret) ++ return ret; ++ ++ ret = clk_sleeping_gpio_gate_prepare(hw); ++ if (ret) ++ clk_gated_fixed_unprepare(hw); ++ ++ return ret; ++} ++ ++static void clk_sleeping_gated_fixed_unprepare(struct clk_hw *hw) ++{ ++ clk_gated_fixed_unprepare(hw); ++ clk_sleeping_gpio_gate_unprepare(hw); ++} ++ ++/* ++ * Fixed gated clock with non-sleeping gpio. ++ * ++ * Enabling the supply regulator and switching the enable-gpio happens ++ * both in the prepare step. ++ * is_prepared only needs to check the gpio state, as toggling the ++ * gpio is the last step when preparing. ++ */ ++static const struct clk_ops clk_sleeping_gated_fixed_ops = { ++ .prepare = clk_sleeping_gated_fixed_prepare, ++ .unprepare = clk_sleeping_gated_fixed_unprepare, ++ .is_prepared = clk_sleeping_gpio_gate_is_prepared, ++ .recalc_rate = clk_gated_fixed_recalc_rate, ++}; ++ ++static int clk_gated_fixed_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct clk_gated_fixed *clk; ++ const struct clk_ops *ops; ++ const char *clk_name; ++ u32 rate; ++ int ret; ++ ++ clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL); ++ if (!clk) ++ return -ENOMEM; ++ ++ ret = device_property_read_u32(dev, "clock-frequency", &rate); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to get clock-frequency\n"); ++ clk->rate = rate; ++ ++ ret = device_property_read_string(dev, "clock-output-names", &clk_name); ++ if (ret) ++ clk_name = fwnode_get_name(dev->fwnode); ++ ++ clk->supply = devm_regulator_get_optional(dev, "vdd"); ++ if (IS_ERR(clk->supply)) { ++ if (PTR_ERR(clk->supply) != -ENODEV) ++ return dev_err_probe(dev, PTR_ERR(clk->supply), ++ "Failed to get regulator\n"); ++ clk->supply = NULL; ++ } ++ ++ clk->clk_gpio.gpiod = devm_gpiod_get_optional(dev, "enable", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(clk->clk_gpio.gpiod)) ++ return dev_err_probe(dev, PTR_ERR(clk->clk_gpio.gpiod), ++ "Failed to get gpio\n"); ++ ++ if (gpiod_cansleep(clk->clk_gpio.gpiod)) ++ ops = &clk_sleeping_gated_fixed_ops; ++ else ++ ops = &clk_gated_fixed_ops; ++ ++ clk->clk_gpio.hw.init = CLK_HW_INIT_NO_PARENT(clk_name, ops, 0); ++ ++ /* register the clock */ ++ ret = devm_clk_hw_register(dev, &clk->clk_gpio.hw); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to register clock\n"); ++ ++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, ++ &clk->clk_gpio.hw); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to register clock provider\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id gated_fixed_clk_match_table[] = { ++ { .compatible = "gated-fixed-clock" }, ++ { /* sentinel */ } ++}; ++ ++static struct platform_driver gated_fixed_clk_driver = { ++ .probe = clk_gated_fixed_probe, ++ .driver = { ++ .name = "gated-fixed-clk", ++ .of_match_table = gated_fixed_clk_match_table, ++ }, ++}; ++builtin_platform_driver(gated_fixed_clk_driver); diff --git a/target/linux/generic/backport-6.12/810-v6.14-gpio-regmap-Use-generic-request-free-ops.patch b/target/linux/generic/backport-6.12/810-v6.14-gpio-regmap-Use-generic-request-free-ops.patch new file mode 100644 index 00000000000..d696acfea4d --- /dev/null +++ b/target/linux/generic/backport-6.12/810-v6.14-gpio-regmap-Use-generic-request-free-ops.patch @@ -0,0 +1,30 @@ +From b0fa00fe38f673c986633c11087274deeb7ce7b0 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Tue, 7 Jan 2025 21:16:20 +0100 +Subject: [PATCH] gpio: regmap: Use generic request/free ops + +Set the gpiochip request and free ops to the generic implementations. +This way a user can provide a gpio-ranges property defined for a pinmux, +easing muxing of gpio functions. Provided that the pin controller +implementents the pinmux op .gpio_request_enable(), pins will +automatically be muxed to their GPIO function when requested. + +Signed-off-by: Sander Vanheule +Acked-by: Michael Walle +Link: https://lore.kernel.org/r/20250107201621.12467-1-sander@svanheule.net +Signed-off-by: Bartosz Golaszewski +--- + drivers/gpio/gpio-regmap.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/gpio/gpio-regmap.c ++++ b/drivers/gpio/gpio-regmap.c +@@ -276,6 +276,8 @@ struct gpio_regmap *gpio_regmap_register + chip->label = config->label ?: dev_name(config->parent); + chip->can_sleep = regmap_might_sleep(config->regmap); + ++ chip->request = gpiochip_generic_request; ++ chip->free = gpiochip_generic_free; + chip->get = gpio_regmap_get; + if (gpio->reg_set_base && gpio->reg_clr_base) + chip->set = gpio_regmap_set_with_clear; diff --git a/target/linux/generic/backport-6.12/813-v6.16-pinctrl-aw9523-fix-can_sleep-flag-for-GPIO-chip.patch b/target/linux/generic/backport-6.12/813-v6.16-pinctrl-aw9523-fix-can_sleep-flag-for-GPIO-chip.patch new file mode 100644 index 00000000000..97b39215e0a --- /dev/null +++ b/target/linux/generic/backport-6.12/813-v6.16-pinctrl-aw9523-fix-can_sleep-flag-for-GPIO-chip.patch @@ -0,0 +1,28 @@ +From 5285b5ed04ab6ad40f7b654eefbccd6ae8cbf415 Mon Sep 17 00:00:00 2001 +From: Milan Krstic +Date: Thu, 3 Jul 2025 14:30:39 +0000 +Subject: [PATCH] pinctrl: aw9523: fix can_sleep flag for GPIO chip + +The GPIO expander is connected via I2C, thus the can_sleep flag has to +be set to true. This fixes spurious "scheduling while atomic" bugs +in the kernel ringbuffer. + +Signed-off-by: David Bauer +Signed-off-by: Milan Krstic +Link: https://lore.kernel.org/20250703143039.5809-1-milan.krstic@gmail.com +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/pinctrl-aw9523.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/pinctrl/pinctrl-aw9523.c ++++ b/drivers/pinctrl/pinctrl-aw9523.c +@@ -784,7 +784,7 @@ static int aw9523_init_gpiochip(struct a + gc->set_config = gpiochip_generic_config; + gc->parent = dev; + gc->owner = THIS_MODULE; +- gc->can_sleep = false; ++ gc->can_sleep = true; + + return 0; + } diff --git a/target/linux/generic/backport-6.12/820-v6.15-power-supply-sysfs-remove-duplicate-nul-termination-to-fix-build-with-GCC15.patch b/target/linux/generic/backport-6.12/820-v6.15-power-supply-sysfs-remove-duplicate-nul-termination-to-fix-build-with-GCC15.patch new file mode 100644 index 00000000000..48f9c1c2648 --- /dev/null +++ b/target/linux/generic/backport-6.12/820-v6.15-power-supply-sysfs-remove-duplicate-nul-termination-to-fix-build-with-GCC15.patch @@ -0,0 +1,39 @@ +From 77f5bb150132bbbcd6bc37ffdc80c9e140e373a4 Mon Sep 17 00:00:00 2001 +From: Kees Cook +Date: Wed, 16 Apr 2025 15:27:41 -0700 +Subject: [PATCH] power: supply: sysfs: Remove duplicate NUL termination + +GCC 15's new -Wunterminated-string-initialization notices that one of +the sysfs attr strings would lack the implicit trailing NUL byte during +initialization: + +drivers/power/supply/power_supply_sysfs.c:183:57: warning: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (32 chars into 31 available) [-Wunterminated-string-initialization] + 183 | POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD), + | ^ +drivers/power/supply/power_supply_sysfs.c:36:23: note: in definition of macro '_POWER_SUPPLY_ATTR' + 36 | .attr_name = #_name "\0", \ + | ^~~~~ +drivers/power/supply/power_supply_sysfs.c:183:9: note: in expansion of macro 'POWER_SUPPLY_ATTR' + 183 | POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD), + | ^~~~~~~~~~~~~~~~~ + +However, the macro used was explicitly adding a trailing NUL byte (which +is not needed). Remove this to avoid the GCC warning. No binary +differences are seen after this change (there was always run for a NUL +byte, it's just that the _second_ NUL byte was getting truncated). + +Signed-off-by: Kees Cook +Link: https://lore.kernel.org/r/20250416222740.work.569-kees@kernel.org +Signed-off-by: Sebastian Reichel + +--- a/drivers/power/supply/power_supply_sysfs.c ++++ b/drivers/power/supply/power_supply_sysfs.c +@@ -33,7 +33,7 @@ struct power_supply_attr { + [POWER_SUPPLY_PROP_ ## _name] = \ + { \ + .prop_name = #_name, \ +- .attr_name = #_name "\0", \ ++ .attr_name = #_name, \ + .text_values = _text, \ + .text_values_len = _len, \ + } diff --git a/target/linux/generic/backport-6.12/821-01-v7.0-dt-bindings-gpio-add-gpio-line-mux-controller.patch b/target/linux/generic/backport-6.12/821-01-v7.0-dt-bindings-gpio-add-gpio-line-mux-controller.patch new file mode 100644 index 00000000000..12e6fedd013 --- /dev/null +++ b/target/linux/generic/backport-6.12/821-01-v7.0-dt-bindings-gpio-add-gpio-line-mux-controller.patch @@ -0,0 +1,128 @@ +From 2a7618ba8698874e9871a8ec5453e0068e94d9e5 Mon Sep 17 00:00:00 2001 +From: Jonas Jelonek +Date: Sat, 27 Dec 2025 18:01:33 +0000 +Subject: [PATCH] dt-bindings: gpio: add gpio-line-mux controller + +Add dt-schema for a gpio-line-mux controller which exposes virtual +GPIOs for a shared GPIO controlled by a multiplexer, e.g. a gpio-mux. + +The gpio-line-mux controller is a gpio-controller, thus has mostly the +same semantics. However, it requires a mux-control to be specified upon +which it will operate. + +Signed-off-by: Jonas Jelonek +Reviewed-by: Conor Dooley +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/20251227180134.1262138-2-jelonek.jonas@gmail.com +Signed-off-by: Bartosz Golaszewski + +--- /dev/null ++++ b/Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml +@@ -0,0 +1,107 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: GPIO line mux ++ ++maintainers: ++ - Jonas Jelonek ++ ++description: | ++ A GPIO controller to provide virtual GPIOs for a 1-to-many input-only mapping ++ backed by a single shared GPIO and a multiplexer. A simple illustrated ++ example is: ++ ++ +----- A ++ IN / ++ <-----o------- B ++ / |\ ++ | | +----- C ++ | | \ ++ | | +--- D ++ | | ++ M1 M0 ++ ++ MUX CONTROL ++ ++ M1 M0 IN ++ 0 0 A ++ 0 1 B ++ 1 0 C ++ 1 1 D ++ ++ This can be used in case a real GPIO is connected to multiple inputs and ++ controlled by a multiplexer, and another subsystem/driver does not work ++ directly with the multiplexer subsystem. ++ ++properties: ++ compatible: ++ const: gpio-line-mux ++ ++ gpio-controller: true ++ ++ "#gpio-cells": ++ const: 2 ++ ++ gpio-line-mux-states: ++ description: Mux states corresponding to the virtual GPIOs. ++ $ref: /schemas/types.yaml#/definitions/uint32-array ++ ++ gpio-line-names: true ++ ++ mux-controls: ++ maxItems: 1 ++ description: ++ Phandle to the multiplexer to control access to the GPIOs. ++ ++ ngpios: false ++ ++ muxed-gpios: ++ maxItems: 1 ++ description: ++ GPIO which is the '1' in 1-to-many and is shared by the virtual GPIOs ++ and controlled via the mux. ++ ++required: ++ - compatible ++ - gpio-controller ++ - gpio-line-mux-states ++ - mux-controls ++ - muxed-gpios ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ #include ++ ++ sfp_gpio_mux: mux-controller-1 { ++ compatible = "gpio-mux"; ++ mux-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>, ++ <&gpio0 1 GPIO_ACTIVE_HIGH>; ++ #mux-control-cells = <0>; ++ idle-state = ; ++ }; ++ ++ sfp1_gpio: sfp-gpio-1 { ++ compatible = "gpio-line-mux"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ mux-controls = <&sfp_gpio_mux>; ++ muxed-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>; ++ ++ gpio-line-mux-states = <0>, <1>, <3>; ++ }; ++ ++ sfp1: sfp-p1 { ++ compatible = "sff,sfp"; ++ ++ i2c-bus = <&sfp1_i2c>; ++ los-gpios = <&sfp1_gpio 0 GPIO_ACTIVE_HIGH>; ++ mod-def0-gpios = <&sfp1_gpio 1 GPIO_ACTIVE_LOW>; ++ tx-fault-gpios = <&sfp1_gpio 2 GPIO_ACTIVE_HIGH>; ++ }; diff --git a/target/linux/generic/backport-6.12/821-02-v7.0-gpio-add-gpio-line-mux-driver.patch b/target/linux/generic/backport-6.12/821-02-v7.0-gpio-add-gpio-line-mux-driver.patch new file mode 100644 index 00000000000..35696155657 --- /dev/null +++ b/target/linux/generic/backport-6.12/821-02-v7.0-gpio-add-gpio-line-mux-driver.patch @@ -0,0 +1,209 @@ +From 2b03d9a40cd1fea42fd65d2b66df80edc0f374c8 Mon Sep 17 00:00:00 2001 +From: Jonas Jelonek +Date: Sat, 27 Dec 2025 18:01:34 +0000 +Subject: [PATCH] gpio: add gpio-line-mux driver + +Add a new driver which provides a 1-to-many mapping for a single real +GPIO using a multiplexer. Each virtual GPIO corresponds to a multiplexer +state which, if set for the multiplexer, connects the real GPIO to the +corresponding virtual GPIO. + +This can help in various usecases. One practical case is the special +hardware design of the Realtek-based XS1930-10 switch from Zyxel. It +features two SFP+ ports/cages whose signals are wired directly to the +switch SoC. Although Realtek SoCs are short on GPIOs, there are usually +enough the fit the SFP signals without any hacks. + +However, Zyxel did some weird design and connected RX_LOS, MOD_ABS and +TX_FAULT of one SFP cage onto a single GPIO line controlled by a +multiplexer (the same for the other SFP cage). The single multiplexer +controls the lines for both SFP and depending on the state, the +designated 'signal GPIO lines' are connected to one of the three SFP +signals. + +Because the SFP core/driver doesn't support multiplexer but needs single +GPIOs for each of the signals, this driver fills the gap between both. +It registers a gpio_chip, provides multiple virtual GPIOs and sets the +backing multiplexer accordingly. + +Due to several practical issues, this is input-only and doesn't support +IRQs. + +Signed-off-by: Jonas Jelonek +Reviewed-by: Thomas Richard +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/20251227180134.1262138-3-jelonek.jonas@gmail.com +Signed-off-by: Bartosz Golaszewski + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -9704,6 +9704,12 @@ S: Maintained + F: Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.yaml + F: drivers/media/rc/gpio-ir-tx.c + ++GPIO LINE MUX ++M: Jonas Jelonek ++S: Maintained ++F: Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml ++F: drivers/gpio/gpio-line-mux.c ++ + GPIO MOCKUP DRIVER + M: Bamvor Jian Zhang + L: linux-gpio@vger.kernel.org +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -1866,6 +1866,15 @@ config GPIO_LATCH + Say yes here to enable a driver for GPIO multiplexers based on latches + connected to other GPIOs. + ++config GPIO_LINE_MUX ++ tristate "GPIO line mux driver" ++ depends on OF_GPIO ++ select MULTIPLEXER ++ help ++ Say Y here to support the GPIO line mux, which can provide virtual ++ GPIOs backed by a shared real GPIO and a multiplexer in a 1-to-many ++ fashion. ++ + config GPIO_MOCKUP + tristate "GPIO Testing Driver (DEPRECATED)" + select IRQ_SIM +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -84,6 +84,7 @@ obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4x + obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o + obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o + obj-$(CONFIG_GPIO_LATCH) += gpio-latch.o ++obj-$(CONFIG_GPIO_LINE_MUX) += gpio-line-mux.o + obj-$(CONFIG_GPIO_LJCA) += gpio-ljca.o + obj-$(CONFIG_GPIO_LOGICVC) += gpio-logicvc.o + obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o +--- /dev/null ++++ b/drivers/gpio/gpio-line-mux.c +@@ -0,0 +1,126 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * GPIO line mux which acts as virtual gpiochip and provides a 1-to-many ++ * mapping between virtual GPIOs and a real GPIO + multiplexer. ++ * ++ * Copyright (c) 2025 Jonas Jelonek ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MUX_SELECT_DELAY_US 100 ++ ++struct gpio_lmux { ++ struct gpio_chip gc; ++ struct mux_control *mux; ++ struct gpio_desc *muxed_gpio; ++ ++ u32 num_gpio_mux_states; ++ unsigned int gpio_mux_states[] __counted_by(num_gpio_mux_states); ++}; ++ ++static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct gpio_lmux *glm = gpiochip_get_data(gc); ++ int ret; ++ ++ if (offset > gc->ngpio) ++ return -EINVAL; ++ ++ ret = mux_control_select_delay(glm->mux, glm->gpio_mux_states[offset], ++ MUX_SELECT_DELAY_US); ++ if (ret < 0) ++ return ret; ++ ++ ret = gpiod_get_raw_value_cansleep(glm->muxed_gpio); ++ mux_control_deselect(glm->mux); ++ return ret; ++} ++ ++static int gpio_lmux_gpio_set(struct gpio_chip *gc, unsigned int offset, ++ int value) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static int gpio_lmux_gpio_get_direction(struct gpio_chip *gc, ++ unsigned int offset) ++{ ++ return GPIO_LINE_DIRECTION_IN; ++} ++ ++static int gpio_lmux_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct gpio_lmux *glm; ++ unsigned int ngpio; ++ size_t size; ++ int ret; ++ ++ ngpio = device_property_count_u32(dev, "gpio-line-mux-states"); ++ if (!ngpio) ++ return -EINVAL; ++ ++ size = struct_size(glm, gpio_mux_states, ngpio); ++ glm = devm_kzalloc(dev, size, GFP_KERNEL); ++ if (!glm) ++ return -ENOMEM; ++ ++ glm->gc.base = -1; ++ glm->gc.can_sleep = true; ++ glm->gc.fwnode = dev_fwnode(dev); ++ glm->gc.label = dev_name(dev); ++ glm->gc.ngpio = ngpio; ++ glm->gc.owner = THIS_MODULE; ++ glm->gc.parent = dev; ++ ++ glm->gc.get = gpio_lmux_gpio_get; ++ glm->gc.set = gpio_lmux_gpio_set; ++ glm->gc.get_direction = gpio_lmux_gpio_get_direction; ++ ++ glm->mux = devm_mux_control_get(dev, NULL); ++ if (IS_ERR(glm->mux)) ++ return dev_err_probe(dev, PTR_ERR(glm->mux), ++ "could not get mux controller\n"); ++ ++ glm->muxed_gpio = devm_gpiod_get(dev, "muxed", GPIOD_IN); ++ if (IS_ERR(glm->muxed_gpio)) ++ return dev_err_probe(dev, PTR_ERR(glm->muxed_gpio), ++ "could not get muxed-gpio\n"); ++ ++ glm->num_gpio_mux_states = ngpio; ++ ret = device_property_read_u32_array(dev, "gpio-line-mux-states", ++ &glm->gpio_mux_states[0], ngpio); ++ if (ret) ++ return dev_err_probe(dev, ret, "could not get mux states\n"); ++ ++ ret = devm_gpiochip_add_data(dev, &glm->gc, glm); ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to add gpiochip\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id gpio_lmux_of_match[] = { ++ { .compatible = "gpio-line-mux" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, gpio_lmux_of_match); ++ ++static struct platform_driver gpio_lmux_driver = { ++ .driver = { ++ .name = "gpio-line-mux", ++ .of_match_table = gpio_lmux_of_match, ++ }, ++ .probe = gpio_lmux_probe, ++}; ++module_platform_driver(gpio_lmux_driver); ++ ++MODULE_AUTHOR("Jonas Jelonek "); ++MODULE_DESCRIPTION("GPIO line mux driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.12/821-03-v7.0-gpio-line-mux-remove-bits-already-handled-by-GPIO-co.patch b/target/linux/generic/backport-6.12/821-03-v7.0-gpio-line-mux-remove-bits-already-handled-by-GPIO-co.patch new file mode 100644 index 00000000000..717421ea629 --- /dev/null +++ b/target/linux/generic/backport-6.12/821-03-v7.0-gpio-line-mux-remove-bits-already-handled-by-GPIO-co.patch @@ -0,0 +1,52 @@ +From e034e058897a12bc856f8b22d1796964c742f732 Mon Sep 17 00:00:00 2001 +From: Bartosz Golaszewski +Date: Wed, 7 Jan 2026 09:58:33 +0100 +Subject: [PATCH] gpio: line-mux: remove bits already handled by GPIO core + +GPIO core already handles checking the offset against the number of +GPIOs as well as missing any of the GPIO chip callbacks. Remove the +unnecessary bits. + +Also, the offset check was off-by-one as reported by Dan. + +Fixes: 2b03d9a40cd1 ("gpio: add gpio-line-mux driver") +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/all/aV4b6GAGz1zyf8Xy@stanley.mountain/ +Tested-by: Jonas Jelonek +Reviewed-by: Jonas Jelonek +Link: https://lore.kernel.org/r/20260107085833.17338-1-bartosz.golaszewski@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski + +--- a/drivers/gpio/gpio-line-mux.c ++++ b/drivers/gpio/gpio-line-mux.c +@@ -29,9 +29,6 @@ static int gpio_lmux_gpio_get(struct gpi + struct gpio_lmux *glm = gpiochip_get_data(gc); + int ret; + +- if (offset > gc->ngpio) +- return -EINVAL; +- + ret = mux_control_select_delay(glm->mux, glm->gpio_mux_states[offset], + MUX_SELECT_DELAY_US); + if (ret < 0) +@@ -42,12 +39,6 @@ static int gpio_lmux_gpio_get(struct gpi + return ret; + } + +-static int gpio_lmux_gpio_set(struct gpio_chip *gc, unsigned int offset, +- int value) +-{ +- return -EOPNOTSUPP; +-} +- + static int gpio_lmux_gpio_get_direction(struct gpio_chip *gc, + unsigned int offset) + { +@@ -80,7 +71,6 @@ static int gpio_lmux_probe(struct platfo + glm->gc.parent = dev; + + glm->gc.get = gpio_lmux_gpio_get; +- glm->gc.set = gpio_lmux_gpio_set; + glm->gc.get_direction = gpio_lmux_gpio_get_direction; + + glm->mux = devm_mux_control_get(dev, NULL); diff --git a/target/linux/generic/backport-6.12/822-v6.19-ALSA-usb-audio-Convert-to-common-field_-get-prep-hel.patch b/target/linux/generic/backport-6.12/822-v6.19-ALSA-usb-audio-Convert-to-common-field_-get-prep-hel.patch new file mode 100644 index 00000000000..ad4c55e11c9 --- /dev/null +++ b/target/linux/generic/backport-6.12/822-v6.19-ALSA-usb-audio-Convert-to-common-field_-get-prep-hel.patch @@ -0,0 +1,28 @@ +From b1cff2f4b2391a13bd3e9263502072df1ee5d035 Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Thu, 6 Nov 2025 14:34:10 +0100 +Subject: [PATCH] ALSA: usb-audio: Convert to common field_{get,prep}() helpers + +Drop the driver-specific field_get() and field_prep() macros, in favor +of the globally available variants from . + +Signed-off-by: Geert Uytterhoeven +Acked-by: Takashi Iwai +Signed-off-by: Yury Norov (NVIDIA) +--- + sound/usb/mixer_quirks.c | 6 ------ + 1 file changed, 6 deletions(-) + +--- a/sound/usb/mixer_quirks.c ++++ b/sound/usb/mixer_quirks.c +@@ -3367,10 +3367,6 @@ static int snd_bbfpro_controls_create(st + #define RME_DIGIFACE_REGISTER(reg, mask) (((reg) << 16) | (mask)) + #define RME_DIGIFACE_INVERT BIT(31) + +-/* Nonconst helpers */ +-#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) +-#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) +- + static int snd_rme_digiface_write_reg(struct snd_kcontrol *kcontrol, int item, u16 mask, u16 val) + { + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); diff --git a/target/linux/generic/backport-6.12/823-v6.19-clk-at91-Convert-to-common-field_-get-prep-helpers.patch b/target/linux/generic/backport-6.12/823-v6.19-clk-at91-Convert-to-common-field_-get-prep-helpers.patch new file mode 100644 index 00000000000..fa32ad644d2 --- /dev/null +++ b/target/linux/generic/backport-6.12/823-v6.19-clk-at91-Convert-to-common-field_-get-prep-helpers.patch @@ -0,0 +1,40 @@ +From 0f8407a1f1c795c417e4c7750654a6024a3ec68b Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Thu, 6 Nov 2025 14:34:02 +0100 +Subject: [PATCH] clk: at91: Convert to common field_{get,prep}() helpers + +Drop the driver-specific field_get() and field_prep() macros, in favor +of the globally available variants from . + +Signed-off-by: Geert Uytterhoeven +Acked-by: Alexandre Belloni +Acked-by: Stephen Boyd +Acked-by: Claudiu Beznea +Signed-off-by: Yury Norov (NVIDIA) +--- + drivers/clk/at91/clk-peripheral.c | 1 + + drivers/clk/at91/pmc.h | 5 ----- + 2 files changed, 1 insertion(+), 5 deletions(-) + +--- a/drivers/clk/at91/clk-peripheral.c ++++ b/drivers/clk/at91/clk-peripheral.c +@@ -3,6 +3,7 @@ + * Copyright (C) 2013 Boris BREZILLON + */ + ++#include + #include + #include + #include +--- a/drivers/clk/at91/pmc.h ++++ b/drivers/clk/at91/pmc.h +@@ -116,9 +116,6 @@ struct at91_clk_pms { + unsigned int parent; + }; + +-#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) +-#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) +- + #define ndck(a, s) (a[s - 1].id + 1) + #define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1) + diff --git a/target/linux/generic/backport-6.12/824-v6.19-iio-mlx90614-Convert-to-common-field_-get-prep-helpe.patch b/target/linux/generic/backport-6.12/824-v6.19-iio-mlx90614-Convert-to-common-field_-get-prep-helpe.patch new file mode 100644 index 00000000000..8b502ec61ae --- /dev/null +++ b/target/linux/generic/backport-6.12/824-v6.19-iio-mlx90614-Convert-to-common-field_-get-prep-helpe.patch @@ -0,0 +1,37 @@ +From 1fe1c28a108e4953f083c0106575ee0eccc296ae Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Thu, 6 Nov 2025 14:34:07 +0100 +Subject: [PATCH] iio: mlx90614: Convert to common field_{get,prep}() helpers + +Drop the driver-specific field_get() and field_prep() macros, in favor +of the globally available variants from . + +Signed-off-by: Geert Uytterhoeven +Acked-by: Jonathan Cameron +Acked-by: Crt Mori +Signed-off-by: Yury Norov (NVIDIA) +--- + drivers/iio/temperature/mlx90614.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +--- a/drivers/iio/temperature/mlx90614.c ++++ b/drivers/iio/temperature/mlx90614.c +@@ -22,6 +22,7 @@ + * the "wakeup" GPIO is not given, power management will be disabled. + */ + ++#include + #include + #include + #include +@@ -68,10 +69,6 @@ + #define MLX90614_CONST_SCALE 20 /* Scale in milliKelvin (0.02 * 1000) */ + #define MLX90614_CONST_FIR 0x7 /* Fixed value for FIR part of low pass filter */ + +-/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */ +-#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) +-#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) +- + struct mlx_chip_info { + /* EEPROM offsets with 16-bit data, MSB first */ + /* emissivity correction coefficient */ diff --git a/target/linux/generic/backport-6.12/825-v6.19-pinctrl-ma35-Convert-to-common-field_-get-prep-helpe.patch b/target/linux/generic/backport-6.12/825-v6.19-pinctrl-ma35-Convert-to-common-field_-get-prep-helpe.patch new file mode 100644 index 00000000000..566a00319fa --- /dev/null +++ b/target/linux/generic/backport-6.12/825-v6.19-pinctrl-ma35-Convert-to-common-field_-get-prep-helpe.patch @@ -0,0 +1,28 @@ +From bb0e7fda87753a973cb4a86c22905b1177f00d4e Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Thu, 6 Nov 2025 14:34:08 +0100 +Subject: [PATCH] pinctrl: ma35: Convert to common field_{get,prep}() helpers + +Drop the driver-specific field_get() and field_prep() macros, in favor +of the globally available variants from . + +Signed-off-by: Geert Uytterhoeven +Reviewed-by: Linus Walleij +Signed-off-by: Yury Norov (NVIDIA) +--- + drivers/pinctrl/nuvoton/pinctrl-ma35.c | 6 ------ + 1 file changed, 6 deletions(-) + +--- a/drivers/pinctrl/nuvoton/pinctrl-ma35.c ++++ b/drivers/pinctrl/nuvoton/pinctrl-ma35.c +@@ -81,10 +81,6 @@ + #define MVOLT_1800 0 + #define MVOLT_3300 1 + +-/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */ +-#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) +-#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) +- + static const char * const gpio_group_name[] = { + "gpioa", "gpiob", "gpioc", "gpiod", "gpioe", "gpiof", "gpiog", + "gpioh", "gpioi", "gpioj", "gpiok", "gpiol", "gpiom", "gpion", diff --git a/target/linux/generic/backport-6.12/891-v6.14-dt-bindings-leds-Add-LED1202-LED-Controller.patch b/target/linux/generic/backport-6.12/891-v6.14-dt-bindings-leds-Add-LED1202-LED-Controller.patch new file mode 100644 index 00000000000..9901d13241b --- /dev/null +++ b/target/linux/generic/backport-6.12/891-v6.14-dt-bindings-leds-Add-LED1202-LED-Controller.patch @@ -0,0 +1,160 @@ +From 599b92fd0efa8b7c43e7f58c9dd0f7951f7cbf09 Mon Sep 17 00:00:00 2001 +From: Vicentiu Galanopulo +Date: Wed, 18 Dec 2024 18:33:58 +0000 +Subject: dt-bindings: leds: Add LED1202 LED Controller + +The LED1202 is a 12-channel low quiescent current LED driver with: + * Supply range from 2.6 V to 5 V + * 20 mA current capability per channel + * 1.8 V compatible I2C control interface + * 8-bit analog dimming individual control + * 12-bit local PWM resolution + * 8 programmable patterns + +If the led node is present in the controller then the channel is +set to active. + +Signed-off-by: Vicentiu Galanopulo +Reviewed-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20241218183401.41687-3-vicentiu.galanopulo@remote-tech.co.uk +Signed-off-by: Lee Jones +--- + .../devicetree/bindings/leds/st,led1202.yaml | 132 ++++++++++++++++++ + 1 file changed, 132 insertions(+) + create mode 100644 Documentation/devicetree/bindings/leds/st,led1202.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/leds/st,led1202.yaml +@@ -0,0 +1,132 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/leds/st,led1202.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: ST LED1202 LED controllers ++ ++maintainers: ++ - Vicentiu Galanopulo ++ ++description: | ++ The LED1202 is a 12-channel low quiescent current LED controller ++ programmable via I2C; The output current can be adjusted separately ++ for each channel by 8-bit analog and 12-bit digital dimming control. ++ Datasheet available at ++ https://www.st.com/en/power-management/led1202.html ++ ++properties: ++ compatible: ++ const: st,led1202 ++ ++ reg: ++ maxItems: 1 ++ ++ "#address-cells": ++ const: 1 ++ ++ "#size-cells": ++ const: 0 ++ ++patternProperties: ++ "^led@[0-9a-f]$": ++ type: object ++ $ref: common.yaml# ++ unevaluatedProperties: false ++ ++ properties: ++ reg: ++ minimum: 0 ++ maximum: 11 ++ ++ required: ++ - reg ++ ++required: ++ - compatible ++ - reg ++ - "#address-cells" ++ - "#size-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ ++ i2c { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ led-controller@58 { ++ compatible = "st,led1202"; ++ reg = <0x58>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ led@0 { ++ reg = <0x0>; ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ function-enumerator = <1>; ++ }; ++ ++ led@1 { ++ reg = <0x1>; ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ function-enumerator = <2>; ++ }; ++ ++ led@2 { ++ reg = <0x2>; ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ function-enumerator = <3>; ++ }; ++ ++ led@3 { ++ reg = <0x3>; ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ function-enumerator = <4>; ++ }; ++ ++ led@4 { ++ reg = <0x4>; ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ function-enumerator = <5>; ++ }; ++ ++ led@5 { ++ reg = <0x5>; ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ function-enumerator = <6>; ++ }; ++ ++ led@6 { ++ reg = <0x6>; ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ function-enumerator = <7>; ++ }; ++ ++ led@7 { ++ reg = <0x7>; ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ function-enumerator = <8>; ++ }; ++ ++ led@8 { ++ reg = <0x8>; ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ function-enumerator = <9>; ++ }; ++ }; ++ }; ++... diff --git a/target/linux/generic/backport-6.12/892-v6.14-leds-Add-LED1202-I2C-driver.patch b/target/linux/generic/backport-6.12/892-v6.14-leds-Add-LED1202-I2C-driver.patch new file mode 100644 index 00000000000..ee2ea076fa2 --- /dev/null +++ b/target/linux/generic/backport-6.12/892-v6.14-leds-Add-LED1202-I2C-driver.patch @@ -0,0 +1,475 @@ +From 939757aafeb9c266dda37657ee5f7a73ffd35ae2 Mon Sep 17 00:00:00 2001 +From: Vicentiu Galanopulo +Date: Wed, 18 Dec 2024 18:33:59 +0000 +Subject: leds: Add LED1202 I2C driver + +The output current can be adjusted separately for each channel by 8-bit +analog (current sink input) and 12-bit digital (PWM) dimming control. The +LED1202 implements 12 low-side current generators with independent dimming +control. +Internal volatile memory allows the user to store up to 8 different patterns, +each pattern is a particular output configuration in terms of PWM +duty-cycle (on 4096 steps). Analog dimming (on 256 steps) is per channel but +common to all patterns. Each device tree LED node will have a corresponding +entry in /sys/class/leds with the label name. The brightness property +corresponds to the per channel analog dimming, while the patterns[1-8] to the +PWM dimming control. + +Signed-off-by: Vicentiu Galanopulo +Link: https://lore.kernel.org/r/20241218183401.41687-4-vicentiu.galanopulo@remote-tech.co.uk +Signed-off-by: Lee Jones +--- + drivers/leds/Kconfig | 10 + + drivers/leds/Makefile | 1 + + drivers/leds/leds-st1202.c | 416 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 427 insertions(+) + create mode 100644 drivers/leds/leds-st1202.c + +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -931,6 +931,16 @@ config LEDS_LM36274 + Say Y to enable the LM36274 LED driver for TI LMU devices. + This supports the LED device LM36274. + ++config LEDS_ST1202 ++ tristate "LED Support for STMicroelectronics LED1202 I2C chips" ++ depends on LEDS_CLASS ++ depends on I2C ++ depends on OF ++ select LEDS_TRIGGERS ++ help ++ Say Y to enable support for LEDs connected to LED1202 ++ LED driver chips accessed via the I2C bus. ++ + config LEDS_TPS6105X + tristate "LED support for TI TPS6105X" + depends on LEDS_CLASS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -81,6 +81,7 @@ obj-$(CONFIG_LEDS_POWERNV) += leds-powe + obj-$(CONFIG_LEDS_PWM) += leds-pwm.o + obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o + obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o ++obj-$(CONFIG_LEDS_ST1202) += leds-st1202.o + obj-$(CONFIG_LEDS_SUN50I_A100) += leds-sun50i-a100.o + obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o + obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o +--- /dev/null ++++ b/drivers/leds/leds-st1202.c +@@ -0,0 +1,416 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * LED driver for STMicroelectronics LED1202 chip ++ * ++ * Copyright (C) 2024 Remote-Tech Ltd. UK ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ST1202_CHAN_DISABLE_ALL 0x00 ++#define ST1202_CHAN_ENABLE_HIGH 0x03 ++#define ST1202_CHAN_ENABLE_LOW 0x02 ++#define ST1202_CONFIG_REG 0x04 ++/* PATS: Pattern sequence feature enable */ ++#define ST1202_CONFIG_REG_PATS BIT(7) ++/* PATSR: Pattern sequence runs (self-clear when sequence is finished) */ ++#define ST1202_CONFIG_REG_PATSR BIT(6) ++#define ST1202_CONFIG_REG_SHFT BIT(3) ++#define ST1202_DEV_ENABLE 0x01 ++#define ST1202_DEV_ENABLE_ON BIT(0) ++#define ST1202_DEV_ENABLE_RESET BIT(7) ++#define ST1202_DEVICE_ID 0x00 ++#define ST1202_ILED_REG0 0x09 ++#define ST1202_MAX_LEDS 12 ++#define ST1202_MAX_PATTERNS 8 ++#define ST1202_MILLIS_PATTERN_DUR_MAX 5660 ++#define ST1202_MILLIS_PATTERN_DUR_MIN 22 ++#define ST1202_PATTERN_DUR 0x16 ++#define ST1202_PATTERN_PWM 0x1E ++#define ST1202_PATTERN_REP 0x15 ++ ++struct st1202_led { ++ struct fwnode_handle *fwnode; ++ struct led_classdev led_cdev; ++ struct st1202_chip *chip; ++ bool is_active; ++ int led_num; ++}; ++ ++struct st1202_chip { ++ struct i2c_client *client; ++ struct mutex lock; ++ struct st1202_led leds[ST1202_MAX_LEDS]; ++}; ++ ++static struct st1202_led *cdev_to_st1202_led(struct led_classdev *cdev) ++{ ++ return container_of(cdev, struct st1202_led, led_cdev); ++} ++ ++static int st1202_read_reg(struct st1202_chip *chip, int reg, uint8_t *val) ++{ ++ struct device *dev = &chip->client->dev; ++ int ret; ++ ++ ret = i2c_smbus_read_byte_data(chip->client, reg); ++ if (ret < 0) { ++ dev_err(dev, "Failed to read register [0x%x]: %d\n", reg, ret); ++ return ret; ++ } ++ ++ *val = (uint8_t)ret; ++ return 0; ++} ++ ++static int st1202_write_reg(struct st1202_chip *chip, int reg, uint8_t val) ++{ ++ struct device *dev = &chip->client->dev; ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(chip->client, reg, val); ++ if (ret != 0) ++ dev_err(dev, "Failed to write %d to register [0x%x]: %d\n", val, reg, ret); ++ ++ return ret; ++} ++ ++static uint8_t st1202_prescalar_to_miliseconds(unsigned int value) ++{ ++ return value / ST1202_MILLIS_PATTERN_DUR_MIN - 1; ++} ++ ++static int st1202_pwm_pattern_write(struct st1202_chip *chip, int led_num, ++ int pattern, unsigned int value) ++{ ++ u8 value_l, value_h; ++ int ret; ++ ++ value_l = (u8)value; ++ value_h = (u8)(value >> 8); ++ ++ /* ++ * Datasheet: Register address low = 1Eh + 2*(xh) + 18h*(yh), ++ * where x is the channel number (led number) in hexadecimal (x = 00h .. 0Bh) ++ * and y is the pattern number in hexadecimal (y = 00h .. 07h) ++ */ ++ ret = st1202_write_reg(chip, (ST1202_PATTERN_PWM + (led_num * 2) + 0x18 * pattern), ++ value_l); ++ if (ret != 0) ++ return ret; ++ ++ /* ++ * Datasheet: Register address high = 1Eh + 01h + 2(xh) +18h*(yh), ++ * where x is the channel number in hexadecimal (x = 00h .. 0Bh) ++ * and y is the pattern number in hexadecimal (y = 00h .. 07h) ++ */ ++ ret = st1202_write_reg(chip, (ST1202_PATTERN_PWM + 0x1 + (led_num * 2) + 0x18 * pattern), ++ value_h); ++ if (ret != 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int st1202_duration_pattern_write(struct st1202_chip *chip, int pattern, ++ unsigned int value) ++{ ++ return st1202_write_reg(chip, (ST1202_PATTERN_DUR + pattern), ++ st1202_prescalar_to_miliseconds(value)); ++} ++ ++static void st1202_brightness_set(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ struct st1202_led *led = cdev_to_st1202_led(led_cdev); ++ struct st1202_chip *chip = led->chip; ++ ++ guard(mutex)(&chip->lock); ++ ++ st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value); ++} ++ ++static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev) ++{ ++ struct st1202_led *led = cdev_to_st1202_led(led_cdev); ++ struct st1202_chip *chip = led->chip; ++ u8 value = 0; ++ ++ guard(mutex)(&chip->lock); ++ ++ st1202_read_reg(chip, ST1202_ILED_REG0 + led->led_num, &value); ++ ++ return value; ++} ++ ++static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active) ++{ ++ u8 chan_low, chan_high; ++ int ret; ++ ++ guard(mutex)(&chip->lock); ++ ++ if (led_num <= 7) { ++ ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_LOW, &chan_low); ++ if (ret < 0) ++ return ret; ++ ++ chan_low = active ? chan_low | BIT(led_num) : chan_low & ~BIT(led_num); ++ ++ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_LOW, chan_low); ++ if (ret < 0) ++ return ret; ++ ++ } else { ++ ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_HIGH, &chan_high); ++ if (ret < 0) ++ return ret; ++ ++ chan_high = active ? chan_high | (BIT(led_num) >> 8) : ++ chan_high & ~(BIT(led_num) >> 8); ++ ++ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_HIGH, chan_high); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int st1202_led_set(struct led_classdev *ldev, enum led_brightness value) ++{ ++ struct st1202_led *led = cdev_to_st1202_led(ldev); ++ struct st1202_chip *chip = led->chip; ++ ++ return st1202_channel_set(chip, led->led_num, value == LED_OFF ? false : true); ++} ++ ++static int st1202_led_pattern_clear(struct led_classdev *ldev) ++{ ++ struct st1202_led *led = cdev_to_st1202_led(ldev); ++ struct st1202_chip *chip = led->chip; ++ int ret; ++ ++ guard(mutex)(&chip->lock); ++ ++ for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) { ++ ret = st1202_pwm_pattern_write(chip, led->led_num, patt, LED_OFF); ++ if (ret != 0) ++ return ret; ++ ++ ret = st1202_duration_pattern_write(chip, patt, ST1202_MILLIS_PATTERN_DUR_MIN); ++ if (ret != 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int st1202_led_pattern_set(struct led_classdev *ldev, ++ struct led_pattern *pattern, ++ u32 len, int repeat) ++{ ++ struct st1202_led *led = cdev_to_st1202_led(ldev); ++ struct st1202_chip *chip = led->chip; ++ int ret; ++ ++ if (len > ST1202_MAX_PATTERNS) ++ return -EINVAL; ++ ++ guard(mutex)(&chip->lock); ++ ++ for (int patt = 0; patt < len; patt++) { ++ if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN || ++ pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX) ++ return -EINVAL; ++ ++ ret = st1202_pwm_pattern_write(chip, led->led_num, patt, pattern[patt].brightness); ++ if (ret != 0) ++ return ret; ++ ++ ret = st1202_duration_pattern_write(chip, patt, pattern[patt].delta_t); ++ if (ret != 0) ++ return ret; ++ } ++ ++ ret = st1202_write_reg(chip, ST1202_PATTERN_REP, repeat); ++ if (ret != 0) ++ return ret; ++ ++ ret = st1202_write_reg(chip, ST1202_CONFIG_REG, (ST1202_CONFIG_REG_PATSR | ++ ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_SHFT)); ++ if (ret != 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int st1202_dt_init(struct st1202_chip *chip) ++{ ++ struct device *dev = &chip->client->dev; ++ struct st1202_led *led; ++ int err, reg; ++ ++ for_each_available_child_of_node_scoped(dev_of_node(dev), child) { ++ struct led_init_data init_data = {}; ++ ++ err = of_property_read_u32(child, "reg", ®); ++ if (err) ++ return dev_err_probe(dev, err, "Invalid register\n"); ++ ++ led = &chip->leds[reg]; ++ led->is_active = true; ++ led->fwnode = of_fwnode_handle(child); ++ ++ led->led_cdev.max_brightness = U8_MAX; ++ led->led_cdev.brightness_set_blocking = st1202_led_set; ++ led->led_cdev.pattern_set = st1202_led_pattern_set; ++ led->led_cdev.pattern_clear = st1202_led_pattern_clear; ++ led->led_cdev.default_trigger = "pattern"; ++ ++ init_data.fwnode = led->fwnode; ++ init_data.devicename = "st1202"; ++ init_data.default_label = ":"; ++ ++ err = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data); ++ if (err < 0) ++ return dev_err_probe(dev, err, "Failed to register LED class device\n"); ++ ++ led->led_cdev.brightness_set = st1202_brightness_set; ++ led->led_cdev.brightness_get = st1202_brightness_get; ++ } ++ ++ return 0; ++} ++ ++static int st1202_setup(struct st1202_chip *chip) ++{ ++ int ret; ++ ++ guard(mutex)(&chip->lock); ++ ++ /* ++ * Once the supply voltage is applied, the LED1202 executes some internal checks, ++ * afterwords it stops the oscillator and puts the internal LDO in quiescent mode. ++ * To start the device, EN bit must be set inside the “Device Enable” register at ++ * address 01h. As soon as EN is set, the LED1202 loads the adjustment parameters ++ * from the internal non-volatile memory and performs an auto-calibration procedure ++ * in order to increase the output current precision. ++ * Such initialization lasts about 6.5 ms. ++ */ ++ ++ /* Reset the chip during setup */ ++ ret = st1202_write_reg(chip, ST1202_DEV_ENABLE, ST1202_DEV_ENABLE_RESET); ++ if (ret < 0) ++ return ret; ++ ++ /* Enable phase-shift delay feature */ ++ ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT); ++ if (ret < 0) ++ return ret; ++ ++ /* Enable the device */ ++ ret = st1202_write_reg(chip, ST1202_DEV_ENABLE, ST1202_DEV_ENABLE_ON); ++ if (ret < 0) ++ return ret; ++ ++ /* Duration of initialization */ ++ usleep_range(6500, 10000); ++ ++ /* Deactivate all LEDS (channels) and activate only the ones found in Device Tree */ ++ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_LOW, ST1202_CHAN_DISABLE_ALL); ++ if (ret < 0) ++ return ret; ++ ++ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_HIGH, ST1202_CHAN_DISABLE_ALL); ++ if (ret < 0) ++ return ret; ++ ++ ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ++ ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_PATSR); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int st1202_probe(struct i2c_client *client) ++{ ++ struct st1202_chip *chip; ++ struct st1202_led *led; ++ int ret; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return dev_err_probe(&client->dev, -EIO, "SMBUS Byte Data not Supported\n"); ++ ++ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ devm_mutex_init(&client->dev, &chip->lock); ++ chip->client = client; ++ ++ ret = st1202_dt_init(chip); ++ if (ret < 0) ++ return ret; ++ ++ ret = st1202_setup(chip); ++ if (ret < 0) ++ return ret; ++ ++ for (int i = 0; i < ST1202_MAX_LEDS; i++) { ++ led = &chip->leds[i]; ++ led->chip = chip; ++ led->led_num = i; ++ ++ if (!led->is_active) ++ continue; ++ ++ ret = st1202_channel_set(led->chip, led->led_num, true); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to activate LED channel\n"); ++ ++ ret = st1202_led_pattern_clear(&led->led_cdev); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to clear LED pattern\n"); ++ } ++ ++ return 0; ++} ++ ++static const struct i2c_device_id st1202_id[] = { ++ { "st1202-i2c" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(i2c, st1202_id); ++ ++static const struct of_device_id st1202_dt_ids[] = { ++ { .compatible = "st,led1202" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, st1202_dt_ids); ++ ++static struct i2c_driver st1202_driver = { ++ .driver = { ++ .name = "leds-st1202", ++ .of_match_table = of_match_ptr(st1202_dt_ids), ++ }, ++ .probe = st1202_probe, ++ .id_table = st1202_id, ++}; ++module_i2c_driver(st1202_driver); ++ ++MODULE_AUTHOR("Remote Tech LTD"); ++MODULE_DESCRIPTION("STMicroelectronics LED1202 : 12-channel constant current LED driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.12/893-v6.14-leds_st1202-Fix-NULL-pointer-access-error.patch b/target/linux/generic/backport-6.12/893-v6.14-leds_st1202-Fix-NULL-pointer-access-error.patch new file mode 100644 index 00000000000..39d8ddbf74f --- /dev/null +++ b/target/linux/generic/backport-6.12/893-v6.14-leds_st1202-Fix-NULL-pointer-access-error.patch @@ -0,0 +1,73 @@ +From c72e455b89f216b43cd0dbb518036ec4c98f5c46 Mon Sep 17 00:00:00 2001 +From: Manuel Fombuena +Date: Tue, 25 Feb 2025 22:01:02 +0000 +Subject: leds: leds-st1202: Fix NULL pointer access on race condition + +st1202_dt_init() calls devm_led_classdev_register_ext() before the +internal data structures are properly set up, so the LEDs become visible +to user space while being partially initialized, leading to a window +where trying to access them causes a NULL pointer access. + +Move devm_led_classdev_register_ext() from DT initialization +to the end of the probe function when DT and hardware are fully +initialized and ready to interact with user space. + +Fixes: 259230378c65 ("leds: Add LED1202 I2C driver") +Signed-off-by: Manuel Fombuena +Link: https://lore.kernel.org/r/CWLP123MB54732771AC0CE5491B3C84DCC5C32@CWLP123MB5473.GBRP123.PROD.OUTLOOK.COM +Signed-off-by: Lee Jones +--- + drivers/leds/leds-st1202.c | 21 ++++++++++----------- + 1 file changed, 10 insertions(+), 11 deletions(-) + +--- a/drivers/leds/leds-st1202.c ++++ b/drivers/leds/leds-st1202.c +@@ -261,8 +261,6 @@ static int st1202_dt_init(struct st1202_ + int err, reg; + + for_each_available_child_of_node_scoped(dev_of_node(dev), child) { +- struct led_init_data init_data = {}; +- + err = of_property_read_u32(child, "reg", ®); + if (err) + return dev_err_probe(dev, err, "Invalid register\n"); +@@ -276,15 +274,6 @@ static int st1202_dt_init(struct st1202_ + led->led_cdev.pattern_set = st1202_led_pattern_set; + led->led_cdev.pattern_clear = st1202_led_pattern_clear; + led->led_cdev.default_trigger = "pattern"; +- +- init_data.fwnode = led->fwnode; +- init_data.devicename = "st1202"; +- init_data.default_label = ":"; +- +- err = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data); +- if (err < 0) +- return dev_err_probe(dev, err, "Failed to register LED class device\n"); +- + led->led_cdev.brightness_set = st1202_brightness_set; + led->led_cdev.brightness_get = st1202_brightness_get; + } +@@ -368,6 +357,7 @@ static int st1202_probe(struct i2c_clien + return ret; + + for (int i = 0; i < ST1202_MAX_LEDS; i++) { ++ struct led_init_data init_data = {}; + led = &chip->leds[i]; + led->chip = chip; + led->led_num = i; +@@ -384,6 +374,15 @@ static int st1202_probe(struct i2c_clien + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to clear LED pattern\n"); ++ ++ init_data.fwnode = led->fwnode; ++ init_data.devicename = "st1202"; ++ init_data.default_label = ":"; ++ ++ ret = devm_led_classdev_register_ext(&client->dev, &led->led_cdev, &init_data); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to register LED class device\n"); + } + + return 0; diff --git a/target/linux/generic/backport-6.12/900-v6.14-driver-core-add-a-faux-bus-for-use-when-a-simple-dev.patch b/target/linux/generic/backport-6.12/900-v6.14-driver-core-add-a-faux-bus-for-use-when-a-simple-dev.patch new file mode 100644 index 00000000000..1ada8a6ef46 --- /dev/null +++ b/target/linux/generic/backport-6.12/900-v6.14-driver-core-add-a-faux-bus-for-use-when-a-simple-dev.patch @@ -0,0 +1,386 @@ +From 35fa2d88ca9481e5caf533d58b99ca259c63b2fe Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman +Date: Mon, 10 Feb 2025 13:30:25 +0100 +Subject: [PATCH] driver core: add a faux bus for use when a simple device/bus + is needed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Many drivers abuse the platform driver/bus system as it provides a +simple way to create and bind a device to a driver-specific set of +probe/release functions. Instead of doing that, and wasting all of the +memory associated with a platform device, here is a "faux" bus that +can be used instead. + +Reviewed-by: Jonathan Cameron +Reviewed-by: Danilo Krummrich +Reviewed-by: Lyude Paul +Reviewed-by: Thomas Weißschuh +Reviewed-by: Zijun Hu +Link: https://lore.kernel.org/r/2025021026-atlantic-gibberish-3f0c@gregkh +Signed-off-by: Greg Kroah-Hartman +--- + Documentation/driver-api/infrastructure.rst | 6 + + drivers/base/Makefile | 2 +- + drivers/base/base.h | 1 + + drivers/base/faux.c | 232 ++++++++++++++++++++ + drivers/base/init.c | 1 + + include/linux/device/faux.h | 69 ++++++ + 6 files changed, 310 insertions(+), 1 deletion(-) + create mode 100644 drivers/base/faux.c + create mode 100644 include/linux/device/faux.h + +--- a/Documentation/driver-api/infrastructure.rst ++++ b/Documentation/driver-api/infrastructure.rst +@@ -41,6 +41,12 @@ Device Drivers Base + .. kernel-doc:: drivers/base/class.c + :export: + ++.. kernel-doc:: include/linux/device/faux.h ++ :internal: ++ ++.. kernel-doc:: drivers/base/faux.c ++ :export: ++ + .. kernel-doc:: drivers/base/node.c + :internal: + +--- a/drivers/base/Makefile ++++ b/drivers/base/Makefile +@@ -6,7 +6,7 @@ obj-y := component.o core.o bus.o dd.o + cpu.o firmware.o init.o map.o devres.o \ + attribute_container.o transport_class.o \ + topology.o container.o property.o cacheinfo.o \ +- swnode.o ++ swnode.o faux.o + obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o + obj-$(CONFIG_DEVTMPFS) += devtmpfs.o + obj-y += power/ +--- a/drivers/base/base.h ++++ b/drivers/base/base.h +@@ -138,6 +138,7 @@ int hypervisor_init(void); + static inline int hypervisor_init(void) { return 0; } + #endif + int platform_bus_init(void); ++int faux_bus_init(void); + void cpu_dev_init(void); + void container_dev_init(void); + #ifdef CONFIG_AUXILIARY_BUS +--- /dev/null ++++ b/drivers/base/faux.c +@@ -0,0 +1,232 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2025 Greg Kroah-Hartman ++ * Copyright (c) 2025 The Linux Foundation ++ * ++ * A "simple" faux bus that allows devices to be created and added ++ * automatically to it. This is to be used whenever you need to create a ++ * device that is not associated with any "real" system resources, and do ++ * not want to have to deal with a bus/driver binding logic. It is ++ * intended to be very simple, with only a create and a destroy function ++ * available. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "base.h" ++ ++/* ++ * Internal wrapper structure so we can hold a pointer to the ++ * faux_device_ops for this device. ++ */ ++struct faux_object { ++ struct faux_device faux_dev; ++ const struct faux_device_ops *faux_ops; ++}; ++#define to_faux_object(dev) container_of_const(dev, struct faux_object, faux_dev.dev) ++ ++static struct device faux_bus_root = { ++ .init_name = "faux", ++}; ++ ++static int faux_match(struct device *dev, const struct device_driver *drv) ++{ ++ /* Match always succeeds, we only have one driver */ ++ return 1; ++} ++ ++static int faux_probe(struct device *dev) ++{ ++ struct faux_object *faux_obj = to_faux_object(dev); ++ struct faux_device *faux_dev = &faux_obj->faux_dev; ++ const struct faux_device_ops *faux_ops = faux_obj->faux_ops; ++ int ret = 0; ++ ++ if (faux_ops && faux_ops->probe) ++ ret = faux_ops->probe(faux_dev); ++ ++ return ret; ++} ++ ++static void faux_remove(struct device *dev) ++{ ++ struct faux_object *faux_obj = to_faux_object(dev); ++ struct faux_device *faux_dev = &faux_obj->faux_dev; ++ const struct faux_device_ops *faux_ops = faux_obj->faux_ops; ++ ++ if (faux_ops && faux_ops->remove) ++ faux_ops->remove(faux_dev); ++} ++ ++static const struct bus_type faux_bus_type = { ++ .name = "faux", ++ .match = faux_match, ++ .probe = faux_probe, ++ .remove = faux_remove, ++}; ++ ++static struct device_driver faux_driver = { ++ .name = "faux_driver", ++ .bus = &faux_bus_type, ++ .probe_type = PROBE_FORCE_SYNCHRONOUS, ++}; ++ ++static void faux_device_release(struct device *dev) ++{ ++ struct faux_object *faux_obj = to_faux_object(dev); ++ ++ kfree(faux_obj); ++} ++ ++/** ++ * faux_device_create_with_groups - Create and register with the driver ++ * core a faux device and populate the device with an initial ++ * set of sysfs attributes. ++ * @name: The name of the device we are adding, must be unique for ++ * all faux devices. ++ * @parent: Pointer to a potential parent struct device. If set to ++ * NULL, the device will be created in the "root" of the faux ++ * device tree in sysfs. ++ * @faux_ops: struct faux_device_ops that the new device will call back ++ * into, can be NULL. ++ * @groups: The set of sysfs attributes that will be created for this ++ * device when it is registered with the driver core. ++ * ++ * Create a new faux device and register it in the driver core properly. ++ * If present, callbacks in @faux_ops will be called with the device that ++ * for the caller to do something with at the proper time given the ++ * device's lifecycle. ++ * ++ * Note, when this function is called, the functions specified in struct ++ * faux_ops can be called before the function returns, so be prepared for ++ * everything to be properly initialized before that point in time. ++ * ++ * Return: ++ * * NULL if an error happened with creating the device ++ * * pointer to a valid struct faux_device that is registered with sysfs ++ */ ++struct faux_device *faux_device_create_with_groups(const char *name, ++ struct device *parent, ++ const struct faux_device_ops *faux_ops, ++ const struct attribute_group **groups) ++{ ++ struct faux_object *faux_obj; ++ struct faux_device *faux_dev; ++ struct device *dev; ++ int ret; ++ ++ faux_obj = kzalloc(sizeof(*faux_obj), GFP_KERNEL); ++ if (!faux_obj) ++ return NULL; ++ ++ /* Save off the callbacks so we can use them in the future */ ++ faux_obj->faux_ops = faux_ops; ++ ++ /* Initialize the device portion and register it with the driver core */ ++ faux_dev = &faux_obj->faux_dev; ++ dev = &faux_dev->dev; ++ ++ device_initialize(dev); ++ dev->release = faux_device_release; ++ if (parent) ++ dev->parent = parent; ++ else ++ dev->parent = &faux_bus_root; ++ dev->bus = &faux_bus_type; ++ dev->groups = groups; ++ dev_set_name(dev, "%s", name); ++ ++ ret = device_add(dev); ++ if (ret) { ++ pr_err("%s: device_add for faux device '%s' failed with %d\n", ++ __func__, name, ret); ++ put_device(dev); ++ return NULL; ++ } ++ ++ return faux_dev; ++} ++EXPORT_SYMBOL_GPL(faux_device_create_with_groups); ++ ++/** ++ * faux_device_create - create and register with the driver core a faux device ++ * @name: The name of the device we are adding, must be unique for all ++ * faux devices. ++ * @parent: Pointer to a potential parent struct device. If set to ++ * NULL, the device will be created in the "root" of the faux ++ * device tree in sysfs. ++ * @faux_ops: struct faux_device_ops that the new device will call back ++ * into, can be NULL. ++ * ++ * Create a new faux device and register it in the driver core properly. ++ * If present, callbacks in @faux_ops will be called with the device that ++ * for the caller to do something with at the proper time given the ++ * device's lifecycle. ++ * ++ * Note, when this function is called, the functions specified in struct ++ * faux_ops can be called before the function returns, so be prepared for ++ * everything to be properly initialized before that point in time. ++ * ++ * Return: ++ * * NULL if an error happened with creating the device ++ * * pointer to a valid struct faux_device that is registered with sysfs ++ */ ++struct faux_device *faux_device_create(const char *name, ++ struct device *parent, ++ const struct faux_device_ops *faux_ops) ++{ ++ return faux_device_create_with_groups(name, parent, faux_ops, NULL); ++} ++EXPORT_SYMBOL_GPL(faux_device_create); ++ ++/** ++ * faux_device_destroy - destroy a faux device ++ * @faux_dev: faux device to destroy ++ * ++ * Unregisters and cleans up a device that was created with a call to ++ * faux_device_create() ++ */ ++void faux_device_destroy(struct faux_device *faux_dev) ++{ ++ struct device *dev = &faux_dev->dev; ++ ++ if (!faux_dev) ++ return; ++ ++ device_del(dev); ++ ++ /* The final put_device() will clean up the memory we allocated for this device. */ ++ put_device(dev); ++} ++EXPORT_SYMBOL_GPL(faux_device_destroy); ++ ++int __init faux_bus_init(void) ++{ ++ int ret; ++ ++ ret = device_register(&faux_bus_root); ++ if (ret) { ++ put_device(&faux_bus_root); ++ return ret; ++ } ++ ++ ret = bus_register(&faux_bus_type); ++ if (ret) ++ goto error_bus; ++ ++ ret = driver_register(&faux_driver); ++ if (ret) ++ goto error_driver; ++ ++ return ret; ++ ++error_driver: ++ bus_unregister(&faux_bus_type); ++ ++error_bus: ++ device_unregister(&faux_bus_root); ++ return ret; ++} +--- a/drivers/base/init.c ++++ b/drivers/base/init.c +@@ -32,6 +32,7 @@ void __init driver_init(void) + /* These are also core pieces, but must come after the + * core core pieces. + */ ++ faux_bus_init(); + of_core_init(); + platform_bus_init(); + auxiliary_bus_init(); +--- /dev/null ++++ b/include/linux/device/faux.h +@@ -0,0 +1,69 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2025 Greg Kroah-Hartman ++ * Copyright (c) 2025 The Linux Foundation ++ * ++ * A "simple" faux bus that allows devices to be created and added ++ * automatically to it. This is to be used whenever you need to create a ++ * device that is not associated with any "real" system resources, and do ++ * not want to have to deal with a bus/driver binding logic. It is ++ * intended to be very simple, with only a create and a destroy function ++ * available. ++ */ ++#ifndef _FAUX_DEVICE_H_ ++#define _FAUX_DEVICE_H_ ++ ++#include ++#include ++ ++/** ++ * struct faux_device - a "faux" device ++ * @dev: internal struct device of the object ++ * ++ * A simple faux device that can be created/destroyed. To be used when a ++ * driver only needs to have a device to "hang" something off. This can be ++ * used for downloading firmware or other basic tasks. Use this instead of ++ * a struct platform_device if the device has no resources assigned to ++ * it at all. ++ */ ++struct faux_device { ++ struct device dev; ++}; ++#define to_faux_device(x) container_of_const((x), struct faux_device, dev) ++ ++/** ++ * struct faux_device_ops - a set of callbacks for a struct faux_device ++ * @probe: called when a faux device is probed by the driver core ++ * before the device is fully bound to the internal faux bus ++ * code. If probe succeeds, return 0, otherwise return a ++ * negative error number to stop the probe sequence from ++ * succeeding. ++ * @remove: called when a faux device is removed from the system ++ * ++ * Both @probe and @remove are optional, if not needed, set to NULL. ++ */ ++struct faux_device_ops { ++ int (*probe)(struct faux_device *faux_dev); ++ void (*remove)(struct faux_device *faux_dev); ++}; ++ ++struct faux_device *faux_device_create(const char *name, ++ struct device *parent, ++ const struct faux_device_ops *faux_ops); ++struct faux_device *faux_device_create_with_groups(const char *name, ++ struct device *parent, ++ const struct faux_device_ops *faux_ops, ++ const struct attribute_group **groups); ++void faux_device_destroy(struct faux_device *faux_dev); ++ ++static inline void *faux_device_get_drvdata(const struct faux_device *faux_dev) ++{ ++ return dev_get_drvdata(&faux_dev->dev); ++} ++ ++static inline void faux_device_set_drvdata(struct faux_device *faux_dev, void *data) ++{ ++ dev_set_drvdata(&faux_dev->dev, data); ++} ++ ++#endif /* _FAUX_DEVICE_H_ */ diff --git a/target/linux/generic/backport-6.12/910-v7.0-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch b/target/linux/generic/backport-6.12/910-v7.0-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch new file mode 100644 index 00000000000..49576779e1b --- /dev/null +++ b/target/linux/generic/backport-6.12/910-v7.0-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch @@ -0,0 +1,354 @@ +From 030218dedee2628ea3f035d71431e1b7c4191cfb Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Sat, 31 Jan 2026 18:38:47 +0100 +Subject: [PATCH] crypto: testmgr - Add test vectors for + authenc(hmac(sha384),cbc(aes)) + +Test vectors were generated starting from existing CBC(AES) test vectors +(RFC3602, NIST SP800-38A) and adding HMAC(SHA384) computed with Python +script. Then, the results were double-checked on Mediatek MT7981 (safexcel) +and NXP P2020 (talitos). Both platforms pass self-tests. + +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + crypto/testmgr.c | 7 ++ + crypto/testmgr.h | 311 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 318 insertions(+) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4453,6 +4453,13 @@ static const struct alg_test_desc alg_te + .test = alg_test_null, + .fips_allowed = 1, + }, { ++ .alg = "authenc(hmac(sha384),cbc(aes))", ++ .generic_driver = "authenc(hmac-sha384-lib,cbc(aes-generic))", ++ .test = alg_test_aead, ++ .suite = { ++ .aead = __VECS(hmac_sha384_aes_cbc_tv_temp) ++ } ++ }, { + .alg = "authenc(hmac(sha384),cbc(des))", + .test = alg_test_aead, + .suite = { +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -17773,6 +17773,317 @@ static const struct aead_testvec hmac_sh + }, + }; + ++static const struct aead_testvec hmac_sha384_aes_cbc_tv_temp[] = { ++ { /* RFC 3602 Case 1 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" ++ "\x51\x2e\x03\xd5\x34\x12\x00\x06", ++ .klen = 8 + 48 + 16, ++ .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" ++ "\xb4\x22\xda\x80\x2c\x9f\xac\x41", ++ .assoc = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" ++ "\xb4\x22\xda\x80\x2c\x9f\xac\x41", ++ .alen = 16, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\xe3\x53\x77\x9c\x10\x79\xae\xb8" ++ "\x27\x08\x94\x2d\xbe\x77\x18\x1a" ++ "\x79\x1c\xf1\x22\x95\x80\xe0\x60" ++ "\x7f\xf9\x92\x60\x83\xbd\x60\x9c" ++ "\xf6\x62\x8b\xa9\x7d\x56\xe2\xaf" ++ "\x80\x43\xbc\x41\x4a\x63\x0b\xa0" ++ "\x16\x25\xe2\xfe\x0a\x96\xf6\xa5" ++ "\x6c\x0b\xc2\x53\xb4\x27\xd9\x42", ++ .clen = 16 + 48, ++ }, { /* RFC 3602 Case 2 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ++ "\x40\x41\x42\x43\x44\x45\x46\x47" ++ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" ++ "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" ++ "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", ++ .klen = 8 + 48 + 16, ++ .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" ++ "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", ++ .assoc = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" ++ "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", ++ .alen = 16, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a" ++ "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a" ++ "\x75\x86\x60\x2d\x25\x3c\xff\xf9" ++ "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1" ++ "\x4e\x5b\xa8\x65\x51\xc6\x58\xaf" ++ "\x31\x57\x50\x3d\x01\xa1\xa4\x3f" ++ "\x42\xd1\xd7\x31\x76\x8d\xf8\xc8" ++ "\xe4\xd2\x7e\xc5\x23\xe7\xc6\x2e" ++ "\x2d\xfd\x9d\xc1\xac\x50\x1e\xcf" ++ "\xa0\x10\xeb\x1a\x9c\xb7\xe1\xca", ++ .clen = 32 + 48, ++ }, { /* RFC 3602 Case 3 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd\xee\xff\x11\x22" ++ "\x33\x44\x55\x66\x77\x88\x99\xaa" ++ "\xbb\xcc\xdd\xee\xff\x11\x22\x33" ++ "\x6c\x3e\xa0\x47\x76\x30\xce\x21" ++ "\xa2\xce\x33\x4a\xa7\x46\xc2\xcd", ++ .klen = 8 + 48 + 16, ++ .iv = "\xc7\x82\xdc\x4c\x09\x8c\x66\xcb" ++ "\xd9\xcd\x27\xd8\x25\x68\x2c\x81", ++ .assoc = "\xc7\x82\xdc\x4c\x09\x8c\x66\xcb" ++ "\xd9\xcd\x27\xd8\x25\x68\x2c\x81", ++ .alen = 16, ++ .ptext = "This is a 48-byte message (exactly 3 AES blocks)", ++ .plen = 48, ++ .ctext = "\xd0\xa0\x2b\x38\x36\x45\x17\x53" ++ "\xd4\x93\x66\x5d\x33\xf0\xe8\x86" ++ "\x2d\xea\x54\xcd\xb2\x93\xab\xc7" ++ "\x50\x69\x39\x27\x67\x72\xf8\xd5" ++ "\x02\x1c\x19\x21\x6b\xad\x52\x5c" ++ "\x85\x79\x69\x5d\x83\xba\x26\x84" ++ "\xa1\x52\xe7\xda\xf7\x05\xb6\xca" ++ "\xad\x0f\x51\xed\x5a\xd3\x0f\xdf" ++ "\xde\xeb\x3f\x31\xed\x3a\x43\x93" ++ "\x3b\xb7\xca\xc8\x1b\xe7\x3b\x61" ++ "\x6a\x05\xfd\x2d\x6a\x5c\xb1\x0d" ++ "\x6e\x7a\xeb\x1c\x84\xec\xdb\xde", ++ .clen = 48 + 48, ++ }, { /* RFC 3602 Case 4 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd\xee\xff\x11\x22" ++ "\x33\x44\x55\x66\x77\x88\x99\xaa" ++ "\xbb\xcc\xdd\xee\xff\x11\x22\x33" ++ "\x56\xe4\x7a\x38\xc5\x59\x89\x74" ++ "\xbc\x46\x90\x3d\xba\x29\x03\x49", ++ .klen = 8 + 48 + 16, ++ .iv = "\x8c\xe8\x2e\xef\xbe\xa0\xda\x3c" ++ "\x44\x69\x9e\xd7\xdb\x51\xb7\xd9", ++ .assoc = "\x8c\xe8\x2e\xef\xbe\xa0\xda\x3c" ++ "\x44\x69\x9e\xd7\xdb\x51\xb7\xd9", ++ .alen = 16, ++ .ptext = "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" ++ "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" ++ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" ++ "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" ++ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" ++ "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" ++ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" ++ "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf", ++ .plen = 64, ++ .ctext = "\xc3\x0e\x32\xff\xed\xc0\x77\x4e" ++ "\x6a\xff\x6a\xf0\x86\x9f\x71\xaa" ++ "\x0f\x3a\xf0\x7a\x9a\x31\xa9\xc6" ++ "\x84\xdb\x20\x7e\xb0\xef\x8e\x4e" ++ "\x35\x90\x7a\xa6\x32\xc3\xff\xdf" ++ "\x86\x8b\xb7\xb2\x9d\x3d\x46\xad" ++ "\x83\xce\x9f\x9a\x10\x2e\xe9\x9d" ++ "\x49\xa5\x3e\x87\xf4\xc3\xda\x55" ++ "\x85\x7b\x91\xe0\x29\xeb\xd3\x59" ++ "\x7c\xe3\x67\x14\xbe\x71\x2a\xd2" ++ "\x8a\x1a\xd2\x35\x78\x6b\x69\xba" ++ "\x64\xa5\x04\x00\x19\xc3\x4c\xae" ++ "\x71\xff\x76\x9f\xbb\xc3\x29\x22" ++ "\xc2\xc6\x51\xf1\xe6\x29\x5e\xa5", ++ .clen = 64 + 48, ++ }, { /* RFC 3602 Case 5 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd\xee\xff\x11\x22" ++ "\x33\x44\x55\x66\x77\x88\x99\xaa" ++ "\xbb\xcc\xdd\xee\xff\x11\x22\x33" ++ "\x90\xd3\x82\xb4\x10\xee\xba\x7a" ++ "\xd9\x38\xc4\x6c\xec\x1a\x82\xbf", ++ .klen = 8 + 48 + 16, ++ .iv = "\xe9\x6e\x8c\x08\xab\x46\x57\x63" ++ "\xfd\x09\x8d\x45\xdd\x3f\xf8\x93", ++ .assoc = "\x00\x00\x43\x21\x00\x00\x00\x01" ++ "\xe9\x6e\x8c\x08\xab\x46\x57\x63" ++ "\xfd\x09\x8d\x45\xdd\x3f\xf8\x93", ++ .alen = 24, ++ .ptext = "\x08\x00\x0e\xbd\xa7\x0a\x00\x00" ++ "\x8e\x9c\x08\x3d\xb9\x5b\x07\x00" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x01\x02\x03\x04\x05\x06\x07\x08" ++ "\x09\x0a\x0b\x0c\x0d\x0e\x0e\x01", ++ .plen = 80, ++ .ctext = "\xf6\x63\xc2\x5d\x32\x5c\x18\xc6" ++ "\xa9\x45\x3e\x19\x4e\x12\x08\x49" ++ "\xa4\x87\x0b\x66\xcc\x6b\x99\x65" ++ "\x33\x00\x13\xb4\x89\x8d\xc8\x56" ++ "\xa4\x69\x9e\x52\x3a\x55\xdb\x08" ++ "\x0b\x59\xec\x3a\x8e\x4b\x7e\x52" ++ "\x77\x5b\x07\xd1\xdb\x34\xed\x9c" ++ "\x53\x8a\xb5\x0c\x55\x1b\x87\x4a" ++ "\xa2\x69\xad\xd0\x47\xad\x2d\x59" ++ "\x13\xac\x19\xb7\xcf\xba\xd4\xa6" ++ "\x57\x5f\xb4\xd7\x74\x6f\x18\x97" ++ "\xb7\xde\xfc\xf3\x4e\x0d\x29\x4d" ++ "\xa0\xff\x39\x9e\x2d\xbf\x27\xac" ++ "\x54\xb9\x8a\x3e\xab\x3b\xac\xd3" ++ "\x36\x43\x74\xfc\xc2\x64\x81\x8a" ++ "\x2c\x15\x72\xdf\x3f\x9d\x5b\xa4", ++ .clen = 80 + 48, ++ }, { /* NIST SP800-38A F.2.3 CBC-AES192.Encrypt */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x18" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd\xee\xff\x11\x22" ++ "\x33\x44\x55\x66\x77\x88\x99\xaa" ++ "\xbb\xcc\xdd\xee\xff\x11\x22\x33" ++ "\x8e\x73\xb0\xf7\xda\x0e\x64\x52" ++ "\xc8\x10\xf3\x2b\x80\x90\x79\xe5" ++ "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", ++ .klen = 8 + 48 + 24, ++ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .alen = 16, ++ .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" ++ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" ++ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" ++ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" ++ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" ++ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" ++ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", ++ .plen = 64, ++ .ctext = "\x4f\x02\x1d\xb2\x43\xbc\x63\x3d" ++ "\x71\x78\x18\x3a\x9f\xa0\x71\xe8" ++ "\xb4\xd9\xad\xa9\xad\x7d\xed\xf4" ++ "\xe5\xe7\x38\x76\x3f\x69\x14\x5a" ++ "\x57\x1b\x24\x20\x12\xfb\x7a\xe0" ++ "\x7f\xa9\xba\xac\x3d\xf1\x02\xe0" ++ "\x08\xb0\xe2\x79\x88\x59\x88\x81" ++ "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd" ++ "\x29\x9b\x42\x47\x0b\xbf\xf3\x54" ++ "\x54\x95\xb0\x89\xd5\xa0\xc3\x78" ++ "\x60\x6c\x18\x39\x6d\xc9\xfb\x2a" ++ "\x34\x1c\xed\x95\x10\x1e\x43\x0a" ++ "\x72\xce\x26\xbc\x74\xd9\x6f\xa2" ++ "\xf1\xd9\xd0\xb1\xdf\x3d\x93\x14", ++ .clen = 64 + 48, ++ }, { /* NIST SP800-38A F.2.5 CBC-AES256.Encrypt */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x20" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd\xee\xff\x11\x22" ++ "\x33\x44\x55\x66\x77\x88\x99\xaa" ++ "\xbb\xcc\xdd\xee\xff\x11\x22\x33" ++ "\x60\x3d\xeb\x10\x15\xca\x71\xbe" ++ "\x2b\x73\xae\xf0\x85\x7d\x77\x81" ++ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7" ++ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4", ++ .klen = 8 + 48 + 32, ++ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .alen = 16, ++ .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" ++ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" ++ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" ++ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" ++ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" ++ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" ++ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", ++ .plen = 64, ++ .ctext = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba" ++ "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6" ++ "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d" ++ "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d" ++ "\x39\xf2\x33\x69\xa9\xd9\xba\xcf" ++ "\xa5\x30\xe2\x63\x04\x23\x14\x61" ++ "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc" ++ "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b" ++ "\x9f\x50\xce\x64\xd9\xa3\xc9\x7a" ++ "\x15\x3a\x3d\x46\x9a\x90\xf3\x06" ++ "\x22\xad\xc5\x24\x77\x50\xb8\xfe" ++ "\xbe\x37\x16\x86\x34\x5f\xaf\x97" ++ "\x00\x9d\x86\xc8\x32\x4f\x72\x2f" ++ "\x48\x97\xad\xb6\xb9\x77\x33\xbc", ++ .clen = 64 + 48, ++ }, ++}; ++ + static const struct aead_testvec hmac_sha512_aes_cbc_tv_temp[] = { + { /* RFC 3602 Case 1 */ + #ifdef __LITTLE_ENDIAN diff --git a/target/linux/generic/backport-6.12/911-v7.0-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch b/target/linux/generic/backport-6.12/911-v7.0-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch new file mode 100644 index 00000000000..8649c1e5574 --- /dev/null +++ b/target/linux/generic/backport-6.12/911-v7.0-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch @@ -0,0 +1,328 @@ +From a22d48cbe55814061d46db2f87090ba5e36aaf7f Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Sat, 31 Jan 2026 18:43:03 +0100 +Subject: [PATCH] crypto: testmgr - Add test vectors for + authenc(hmac(sha224),cbc(aes)) + +Test vectors were generated starting from existing CBC(AES) test vectors +(RFC3602, NIST SP800-38A) and adding HMAC(SHA224) computed with Python +script. Then, the results were double-checked on Mediatek MT7981 (safexcel) +and NXP P2020 (talitos). Both platforms pass self-tests. + +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + crypto/testmgr.c | 7 ++ + crypto/testmgr.h | 285 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 292 insertions(+) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4414,6 +4414,13 @@ static const struct alg_test_desc alg_te + .test = alg_test_null, + .fips_allowed = 1, + }, { ++ .alg = "authenc(hmac(sha224),cbc(aes))", ++ .generic_driver = "authenc(hmac-sha224-lib,cbc(aes-generic))", ++ .test = alg_test_aead, ++ .suite = { ++ .aead = __VECS(hmac_sha224_aes_cbc_tv_temp) ++ } ++ }, { + .alg = "authenc(hmac(sha224),cbc(des))", + .test = alg_test_aead, + .suite = { +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -17490,6 +17490,291 @@ static const struct aead_testvec hmac_sh + }, + }; + ++static const struct aead_testvec hmac_sha224_aes_cbc_tv_temp[] = { ++ { /* RFC 3602 Case 1 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00" ++ "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" ++ "\x51\x2e\x03\xd5\x34\x12\x00\x06", ++ .klen = 8 + 28 + 16, ++ .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" ++ "\xb4\x22\xda\x80\x2c\x9f\xac\x41", ++ .assoc = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" ++ "\xb4\x22\xda\x80\x2c\x9f\xac\x41", ++ .alen = 16, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\xe3\x53\x77\x9c\x10\x79\xae\xb8" ++ "\x27\x08\x94\x2d\xbe\x77\x18\x1a" ++ "\x17\xe8\x00\x76\x70\x71\xd1\x72" ++ "\xf8\xd0\x91\x51\x67\xf9\xdf\xd6" ++ "\x0d\x56\x1a\xb3\x52\x19\x85\xae" ++ "\x46\x74\xb6\x98", ++ .clen = 16 + 28, ++ }, { /* RFC 3602 Case 2 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b" ++ "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" ++ "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", ++ .klen = 8 + 28 + 16, ++ .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" ++ "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", ++ .assoc = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" ++ "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", ++ .alen = 16, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a" ++ "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a" ++ "\x75\x86\x60\x2d\x25\x3c\xff\xf9" ++ "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1" ++ "\xa1\x11\xfa\xbb\x1e\x04\x7e\xe7" ++ "\x4c\x5f\x65\xbf\x68\x8d\x33\x9d" ++ "\xbc\x74\x9b\xf3\x15\xf3\x8f\x8d" ++ "\xe8\xaf\x33\xe0", ++ ++ .clen = 32 + 28, ++ }, { /* RFC 3602 Case 3 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd" ++ "\x6c\x3e\xa0\x47\x76\x30\xce\x21" ++ "\xa2\xce\x33\x4a\xa7\x46\xc2\xcd", ++ .klen = 8 + 28 + 16, ++ .iv = "\xc7\x82\xdc\x4c\x09\x8c\x66\xcb" ++ "\xd9\xcd\x27\xd8\x25\x68\x2c\x81", ++ .assoc = "\xc7\x82\xdc\x4c\x09\x8c\x66\xcb" ++ "\xd9\xcd\x27\xd8\x25\x68\x2c\x81", ++ .alen = 16, ++ .ptext = "This is a 48-byte message (exactly 3 AES blocks)", ++ .plen = 48, ++ .ctext = "\xd0\xa0\x2b\x38\x36\x45\x17\x53" ++ "\xd4\x93\x66\x5d\x33\xf0\xe8\x86" ++ "\x2d\xea\x54\xcd\xb2\x93\xab\xc7" ++ "\x50\x69\x39\x27\x67\x72\xf8\xd5" ++ "\x02\x1c\x19\x21\x6b\xad\x52\x5c" ++ "\x85\x79\x69\x5d\x83\xba\x26\x84" ++ "\x60\xb3\xca\x0e\xc1\xfe\xf2\x27" ++ "\x5a\x41\xe4\x99\xa8\x19\x56\xf1" ++ "\x44\x98\x27\x9f\x99\xb0\x4a\xad" ++ "\x4d\xc1\x1e\x88", ++ .clen = 48 + 28, ++ }, { /* RFC 3602 Case 4 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd" ++ "\x56\xe4\x7a\x38\xc5\x59\x89\x74" ++ "\xbc\x46\x90\x3d\xba\x29\x03\x49", ++ .klen = 8 + 28 + 16, ++ .iv = "\x8c\xe8\x2e\xef\xbe\xa0\xda\x3c" ++ "\x44\x69\x9e\xd7\xdb\x51\xb7\xd9", ++ .assoc = "\x8c\xe8\x2e\xef\xbe\xa0\xda\x3c" ++ "\x44\x69\x9e\xd7\xdb\x51\xb7\xd9", ++ .alen = 16, ++ .ptext = "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" ++ "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" ++ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" ++ "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" ++ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" ++ "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" ++ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" ++ "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf", ++ .plen = 64, ++ .ctext = "\xc3\x0e\x32\xff\xed\xc0\x77\x4e" ++ "\x6a\xff\x6a\xf0\x86\x9f\x71\xaa" ++ "\x0f\x3a\xf0\x7a\x9a\x31\xa9\xc6" ++ "\x84\xdb\x20\x7e\xb0\xef\x8e\x4e" ++ "\x35\x90\x7a\xa6\x32\xc3\xff\xdf" ++ "\x86\x8b\xb7\xb2\x9d\x3d\x46\xad" ++ "\x83\xce\x9f\x9a\x10\x2e\xe9\x9d" ++ "\x49\xa5\x3e\x87\xf4\xc3\xda\x55" ++ "\xbb\xe9\x38\xf8\xb9\xbf\xcb\x7b" ++ "\xa8\x22\x91\xea\x1e\xaf\x13\xba" ++ "\x24\x18\x64\x9c\xcb\xb4\xa9\x16" ++ "\x4b\x83\x9c\xec", ++ .clen = 64 + 28, ++ }, { /* RFC 3602 Case 5 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd" ++ "\x90\xd3\x82\xb4\x10\xee\xba\x7a" ++ "\xd9\x38\xc4\x6c\xec\x1a\x82\xbf", ++ .klen = 8 + 28 + 16, ++ .iv = "\xe9\x6e\x8c\x08\xab\x46\x57\x63" ++ "\xfd\x09\x8d\x45\xdd\x3f\xf8\x93", ++ .assoc = "\x00\x00\x43\x21\x00\x00\x00\x01" ++ "\xe9\x6e\x8c\x08\xab\x46\x57\x63" ++ "\xfd\x09\x8d\x45\xdd\x3f\xf8\x93", ++ .alen = 24, ++ .ptext = "\x08\x00\x0e\xbd\xa7\x0a\x00\x00" ++ "\x8e\x9c\x08\x3d\xb9\x5b\x07\x00" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x01\x02\x03\x04\x05\x06\x07\x08" ++ "\x09\x0a\x0b\x0c\x0d\x0e\x0e\x01", ++ .plen = 80, ++ .ctext = "\xf6\x63\xc2\x5d\x32\x5c\x18\xc6" ++ "\xa9\x45\x3e\x19\x4e\x12\x08\x49" ++ "\xa4\x87\x0b\x66\xcc\x6b\x99\x65" ++ "\x33\x00\x13\xb4\x89\x8d\xc8\x56" ++ "\xa4\x69\x9e\x52\x3a\x55\xdb\x08" ++ "\x0b\x59\xec\x3a\x8e\x4b\x7e\x52" ++ "\x77\x5b\x07\xd1\xdb\x34\xed\x9c" ++ "\x53\x8a\xb5\x0c\x55\x1b\x87\x4a" ++ "\xa2\x69\xad\xd0\x47\xad\x2d\x59" ++ "\x13\xac\x19\xb7\xcf\xba\xd4\xa6" ++ "\x04\x5e\x83\x45\xc5\x6a\x5b\xe2" ++ "\x5e\xd8\x59\x06\xbd\xc7\xd2\x9b" ++ "\x0b\x65\x1f\x31\xc7\xe6\x9c\x39" ++ "\xa3\x66\xdb\xb8", ++ .clen = 80 + 28, ++ }, { /* NIST SP800-38A F.2.3 CBC-AES192.Encrypt */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x18" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd" ++ "\x8e\x73\xb0\xf7\xda\x0e\x64\x52" ++ "\xc8\x10\xf3\x2b\x80\x90\x79\xe5" ++ "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", ++ .klen = 8 + 28 + 24, ++ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .alen = 16, ++ .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" ++ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" ++ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" ++ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" ++ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" ++ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" ++ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", ++ .plen = 64, ++ .ctext = "\x4f\x02\x1d\xb2\x43\xbc\x63\x3d" ++ "\x71\x78\x18\x3a\x9f\xa0\x71\xe8" ++ "\xb4\xd9\xad\xa9\xad\x7d\xed\xf4" ++ "\xe5\xe7\x38\x76\x3f\x69\x14\x5a" ++ "\x57\x1b\x24\x20\x12\xfb\x7a\xe0" ++ "\x7f\xa9\xba\xac\x3d\xf1\x02\xe0" ++ "\x08\xb0\xe2\x79\x88\x59\x88\x81" ++ "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd" ++ "\x67\x35\xcd\x86\x94\x51\x3b\x3a" ++ "\xaa\x07\xb1\xed\x18\x55\x62\x01" ++ "\x95\xb2\x53\xb5\x20\x78\x16\xd7" ++ "\xb8\x49\x7f\x96", ++ ++ .clen = 64 + 28, ++ }, { /* NIST SP800-38A F.2.5 CBC-AES256.Encrypt */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x20" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd" ++ "\x60\x3d\xeb\x10\x15\xca\x71\xbe" ++ "\x2b\x73\xae\xf0\x85\x7d\x77\x81" ++ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7" ++ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4", ++ .klen = 8 + 28 + 32, ++ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .alen = 16, ++ .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" ++ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" ++ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" ++ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" ++ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" ++ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" ++ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", ++ .plen = 64, ++ .ctext = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba" ++ "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6" ++ "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d" ++ "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d" ++ "\x39\xf2\x33\x69\xa9\xd9\xba\xcf" ++ "\xa5\x30\xe2\x63\x04\x23\x14\x61" ++ "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc" ++ "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b" ++ "\xe0\xe2\x3d\x3f\x55\x24\x2c\x4d" ++ "\xb9\x13\x2a\xc0\x07\xbb\x3b\xda" ++ "\xfd\xa4\x51\x32\x3f\x44\xb1\x13" ++ "\x98\xf9\xbc\xb9", ++ .clen = 64 + 28, ++ }, ++}; ++ + static const struct aead_testvec hmac_sha256_aes_cbc_tv_temp[] = { + { /* RFC 3602 Case 1 */ + #ifdef __LITTLE_ENDIAN diff --git a/target/linux/generic/backport-6.12/912-v7.0-crypto-testmgr-Add-test-vectors-for-authenc-hmac-md5.patch b/target/linux/generic/backport-6.12/912-v7.0-crypto-testmgr-Add-test-vectors-for-authenc-hmac-md5.patch new file mode 100644 index 00000000000..0e36006d3d4 --- /dev/null +++ b/target/linux/generic/backport-6.12/912-v7.0-crypto-testmgr-Add-test-vectors-for-authenc-hmac-md5.patch @@ -0,0 +1,102 @@ +From dc8f3d9ae804e8bb47dd49b051fe8b303db3b95c Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Sun, 1 Feb 2026 12:27:08 +0100 +Subject: [PATCH] crypto: testmgr - Add test vectors for + authenc(hmac(md5),cbc(des3_ede)) + +Test vector was generated using a software implementation and then double +checked using a hardware implementation on NXP P2020 (talitos). The +encryption part is identical to authenc(hmac(sha1),cbc(des3_ede)), +only HMAC is different. + +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + crypto/testmgr.c | 7 ++++++ + crypto/testmgr.h | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 66 insertions(+) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4375,6 +4375,13 @@ static const struct alg_test_desc alg_te + .cprng = __VECS(ansi_cprng_aes_tv_template) + } + }, { ++ .alg = "authenc(hmac(md5),cbc(des3_ede))", ++ .generic_driver = "authenc(hmac-md5-lib,cbc(des3_ede-generic))", ++ .test = alg_test_aead, ++ .suite = { ++ .aead = __VECS(hmac_md5_des3_ede_cbc_tv_temp) ++ } ++ }, { + .alg = "authenc(hmac(md5),ecb(cipher_null))", + .test = alg_test_aead, + .suite = { +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -19021,6 +19021,65 @@ static const struct aead_testvec hmac_sh + }, + }; + ++static const struct aead_testvec hmac_md5_des3_ede_cbc_tv_temp[] = { ++ { /*Generated with cryptopp*/ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x18" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\xE9\xC0\xFF\x2E\x76\x0B\x64\x24" ++ "\x44\x4D\x99\x5A\x12\xD6\x40\xC0" ++ "\xEA\xC2\x84\xE8\x14\x95\xDB\xE8", ++ .klen = 8 + 16 + 24, ++ .iv = "\x7D\x33\x88\x93\x0F\x93\xB2\x42", ++ .assoc = "\x00\x00\x43\x21\x00\x00\x00\x01" ++ "\x7D\x33\x88\x93\x0F\x93\xB2\x42", ++ .alen = 16, ++ .ptext = "\x6f\x54\x20\x6f\x61\x4d\x79\x6e" ++ "\x53\x20\x63\x65\x65\x72\x73\x74" ++ "\x54\x20\x6f\x6f\x4d\x20\x6e\x61" ++ "\x20\x79\x65\x53\x72\x63\x74\x65" ++ "\x20\x73\x6f\x54\x20\x6f\x61\x4d" ++ "\x79\x6e\x53\x20\x63\x65\x65\x72" ++ "\x73\x74\x54\x20\x6f\x6f\x4d\x20" ++ "\x6e\x61\x20\x79\x65\x53\x72\x63" ++ "\x74\x65\x20\x73\x6f\x54\x20\x6f" ++ "\x61\x4d\x79\x6e\x53\x20\x63\x65" ++ "\x65\x72\x73\x74\x54\x20\x6f\x6f" ++ "\x4d\x20\x6e\x61\x20\x79\x65\x53" ++ "\x72\x63\x74\x65\x20\x73\x6f\x54" ++ "\x20\x6f\x61\x4d\x79\x6e\x53\x20" ++ "\x63\x65\x65\x72\x73\x74\x54\x20" ++ "\x6f\x6f\x4d\x20\x6e\x61\x0a\x79", ++ .plen = 128, ++ .ctext = "\x0e\x2d\xb6\x97\x3c\x56\x33\xf4" ++ "\x67\x17\x21\xc7\x6e\x8a\xd5\x49" ++ "\x74\xb3\x49\x05\xc5\x1c\xd0\xed" ++ "\x12\x56\x5c\x53\x96\xb6\x00\x7d" ++ "\x90\x48\xfc\xf5\x8d\x29\x39\xcc" ++ "\x8a\xd5\x35\x18\x36\x23\x4e\xd7" ++ "\x76\xd1\xda\x0c\x94\x67\xbb\x04" ++ "\x8b\xf2\x03\x6c\xa8\xcf\xb6\xea" ++ "\x22\x64\x47\xaa\x8f\x75\x13\xbf" ++ "\x9f\xc2\xc3\xf0\xc9\x56\xc5\x7a" ++ "\x71\x63\x2e\x89\x7b\x1e\x12\xca" ++ "\xe2\x5f\xaf\xd8\xa4\xf8\xc9\x7a" ++ "\xd6\xf9\x21\x31\x62\x44\x45\xa6" ++ "\xd6\xbc\x5a\xd3\x2d\x54\x43\xcc" ++ "\x9d\xde\xa5\x70\xe9\x42\x45\x8a" ++ "\x6b\xfa\xb1\x91\x13\xb0\xd9\x19" ++ "\x99\x09\xfb\x05\x35\xc8\xcc\x38" ++ "\xc3\x1e\x5e\xe1\xe6\x96\x84\xc8", ++ .clen = 128 + 16, ++ }, ++}; ++ + static const struct aead_testvec hmac_sha1_des3_ede_cbc_tv_temp[] = { + { /*Generated with cryptopp*/ + #ifdef __LITTLE_ENDIAN diff --git a/target/linux/generic/backport-6.12/913-v7.0-crypto-testmgr-allow-authenc-sha224-rfc3686-variant-.patch b/target/linux/generic/backport-6.12/913-v7.0-crypto-testmgr-allow-authenc-sha224-rfc3686-variant-.patch new file mode 100644 index 00000000000..7ea115c3f62 --- /dev/null +++ b/target/linux/generic/backport-6.12/913-v7.0-crypto-testmgr-allow-authenc-sha224-rfc3686-variant-.patch @@ -0,0 +1,31 @@ +From e1dc530d0c082c903e74bf6e16d8a38843d3d1d7 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Thu, 1 Jan 2026 16:25:18 +0100 +Subject: [PATCH] crypto: testmgr - allow authenc(sha224,rfc3686) variant in + fips mode + +The remaining combinations of AES-CTR-RFC3686 and SHA* have already been +marked as allowed in 8888690ef5f7. This commit does the same for SHA224. + +rfc3686(ctr(aes)) is already marked fips compliant, +so these should be fine. + +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + crypto/testmgr.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4440,6 +4440,10 @@ static const struct alg_test_desc alg_te + .aead = __VECS(hmac_sha224_des3_ede_cbc_tv_temp) + } + }, { ++ .alg = "authenc(hmac(sha224),rfc3686(ctr(aes)))", ++ .test = alg_test_null, ++ .fips_allowed = 1, ++ }, { + .alg = "authenc(hmac(sha256),cbc(aes))", + .test = alg_test_aead, + .fips_allowed = 1, diff --git a/target/linux/generic/backport-6.12/914-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-md5.patch b/target/linux/generic/backport-6.12/914-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-md5.patch new file mode 100644 index 00000000000..0c037504d08 --- /dev/null +++ b/target/linux/generic/backport-6.12/914-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-md5.patch @@ -0,0 +1,99 @@ +From 404ba6b46b6e234384b962210a98931f7423f139 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Sat, 7 Feb 2026 15:51:03 +0100 +Subject: [PATCH] crypto: testmgr - Add test vectors for + authenc(hmac(md5),cbc(des)) + +Test vector was generated using a software implementation and then double +checked on Mediatek MT7981 (safexcel) and NXP P2020 (talitos). Both +platforms pass self-tests. + +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + crypto/testmgr.c | 7 ++++++ + crypto/testmgr.h | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 64 insertions(+) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4375,6 +4375,13 @@ static const struct alg_test_desc alg_te + .cprng = __VECS(ansi_cprng_aes_tv_template) + } + }, { ++ .alg = "authenc(hmac(md5),cbc(des))", ++ .generic_driver = "authenc(hmac-md5-lib,cbc(des-generic))", ++ .test = alg_test_aead, ++ .suite = { ++ .aead = __VECS(hmac_md5_des_cbc_tv_temp) ++ } ++ }, { + .alg = "authenc(hmac(md5),cbc(des3_ede))", + .generic_driver = "authenc(hmac-md5-lib,cbc(des3_ede-generic))", + .test = alg_test_aead, +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -18708,6 +18708,63 @@ static const struct aead_testvec hmac_sh + }, + }; + ++static const struct aead_testvec hmac_md5_des_cbc_tv_temp[] = { ++ { /*Generated with cryptopp*/ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x08" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\xE9\xC0\xFF\x2E\x76\x0B\x64\x24", ++ .klen = 8 + 16 + 8, ++ .iv = "\x7D\x33\x88\x93\x0F\x93\xB2\x42", ++ .assoc = "\x00\x00\x43\x21\x00\x00\x00\x01" ++ "\x7D\x33\x88\x93\x0F\x93\xB2\x42", ++ .alen = 16, ++ .ptext = "\x6f\x54\x20\x6f\x61\x4d\x79\x6e" ++ "\x53\x20\x63\x65\x65\x72\x73\x74" ++ "\x54\x20\x6f\x6f\x4d\x20\x6e\x61" ++ "\x20\x79\x65\x53\x72\x63\x74\x65" ++ "\x20\x73\x6f\x54\x20\x6f\x61\x4d" ++ "\x79\x6e\x53\x20\x63\x65\x65\x72" ++ "\x73\x74\x54\x20\x6f\x6f\x4d\x20" ++ "\x6e\x61\x20\x79\x65\x53\x72\x63" ++ "\x74\x65\x20\x73\x6f\x54\x20\x6f" ++ "\x61\x4d\x79\x6e\x53\x20\x63\x65" ++ "\x65\x72\x73\x74\x54\x20\x6f\x6f" ++ "\x4d\x20\x6e\x61\x20\x79\x65\x53" ++ "\x72\x63\x74\x65\x20\x73\x6f\x54" ++ "\x20\x6f\x61\x4d\x79\x6e\x53\x20" ++ "\x63\x65\x65\x72\x73\x74\x54\x20" ++ "\x6f\x6f\x4d\x20\x6e\x61\x0a\x79", ++ .plen = 128, ++ .ctext = "\x70\xd6\xde\x64\x87\x17\xf1\xe8" ++ "\x54\x31\x85\x37\xed\x6b\x01\x8d" ++ "\xe3\xcc\xe0\x1d\x5e\xf3\xfe\xf1" ++ "\x41\xaa\x33\x91\xa7\x7d\x99\x88" ++ "\x4d\x85\x6e\x2f\xa3\x69\xf5\x82" ++ "\x3a\x6f\x25\xcb\x7d\x58\x1f\x9b" ++ "\xaa\x9c\x11\xd5\x76\x67\xce\xde" ++ "\x56\xd7\x5a\x80\x69\xea\x3a\x02" ++ "\xf0\xc7\x7c\xe3\xcb\x40\xe5\x52" ++ "\xd1\x10\x92\x78\x0b\x8e\x5b\xf1" ++ "\xe3\x26\x1f\xe1\x15\x41\xc7\xba" ++ "\x99\xdb\x08\x51\x1c\xd3\x01\xf4" ++ "\x87\x47\x39\xb8\xd2\xdd\xbd\xfb" ++ "\x66\x13\xdf\x1c\x01\x44\xf0\x7a" ++ "\x1a\x6b\x13\xf5\xd5\x0b\xb8\xba" ++ "\x53\xba\xe1\x76\xe3\x82\x07\x86" ++ "\x95\x9e\x7d\x37\x1e\x60\xaf\x7c" ++ "\x53\x12\x61\x68\xef\xb4\x47\xa6", ++ .clen = 128 + 16, ++ }, ++}; ++ + static const struct aead_testvec hmac_sha1_des_cbc_tv_temp[] = { + { /*Generated with cryptopp*/ + #ifdef __LITTLE_ENDIAN diff --git a/target/linux/generic/backport-6.12/915-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch b/target/linux/generic/backport-6.12/915-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch new file mode 100644 index 00000000000..c6018305d47 --- /dev/null +++ b/target/linux/generic/backport-6.12/915-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch @@ -0,0 +1,264 @@ +From 2a2468bf33d3fac5192429207a439d23e615173a Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Thu, 5 Mar 2026 21:08:20 +0100 +Subject: [PATCH 1/5] crypto: testmgr - Add test vectors for + authenc(hmac(sha1),rfc3686(ctr(aes))) + +Test vectors were generated starting from existing RFC3686(CTR(AES)) test +vectors and adding HMAC(SHA1) computed with software implementation. +Then, the results were double-checked on Mediatek MT7986 (safexcel). +Platform pass self-tests. + +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + crypto/testmgr.c | 6 +- + crypto/testmgr.h | 221 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 226 insertions(+), 1 deletion(-) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4425,8 +4425,12 @@ static const struct alg_test_desc alg_te + } + }, { + .alg = "authenc(hmac(sha1),rfc3686(ctr(aes)))", +- .test = alg_test_null, ++ .generic_driver = "authenc(hmac-sha1-lib,rfc3686(ctr(aes-lib)))", ++ .test = alg_test_aead, + .fips_allowed = 1, ++ .suite = { ++ .aead = __VECS(hmac_sha1_aes_ctr_rfc3686_tv_temp) ++ } + }, { + .alg = "authenc(hmac(sha224),cbc(aes))", + .generic_driver = "authenc(hmac-sha224-lib,cbc(aes-generic))", +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -17444,6 +17444,227 @@ static const struct aead_testvec hmac_sh + }, + }; + ++static const struct aead_testvec hmac_sha1_aes_ctr_rfc3686_tv_temp[] = { ++ { /* RFC 3686 Case 1 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00" ++ "\xae\x68\x52\xf8\x12\x10\x67\xcc" ++ "\x4b\xf7\xa5\x76\x55\x77\xf3\x9e" ++ "\x00\x00\x00\x30", ++ .klen = 8 + 20 + 20, ++ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", ++ .assoc = "\x00\x00\x00\x00\x00\x00\x00\x00", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\xe4\x09\x5d\x4f\xb7\xa7\xb3\x79" ++ "\x2d\x61\x75\xa3\x26\x13\x11\xb8" ++ "\x70\xdc\x6b\x62\x43\xa1\x2f\x08" ++ "\xf1\xec\x93\x7d\x69\xb2\x8e\x1f" ++ "\x0a\x97\x39\x86", ++ .clen = 16 + 20, ++ }, { /* RFC 3686 Case 2 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33" ++ "\x7e\x24\x06\x78\x17\xfa\xe0\xd7" ++ "\x43\xd6\xce\x1f\x32\x53\x91\x63" ++ "\x00\x6c\xb6\xdb", ++ .klen = 8 + 20 + 20, ++ .iv = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", ++ .assoc = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\x51\x04\xa1\x06\x16\x8a\x72\xd9" ++ "\x79\x0d\x41\xee\x8e\xda\xd3\x88" ++ "\xeb\x2e\x1e\xfc\x46\xda\x57\xc8" ++ "\xfc\xe6\x30\xdf\x91\x41\xbe\x28" ++ "\x6b\x7b\x4d\x39\x36\x1c\x12\x5f" ++ "\x72\xd2\x88\xb2\x26\xa6\xa6\xb5" ++ "\x1d\x3a\x49\xa6", ++ .clen = 32 + 20, ++ }, { /* RFC 3686 Case 3 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55" ++ "\x76\x91\xbe\x03\x5e\x50\x20\xa8" ++ "\xac\x6e\x61\x85\x29\xf9\xa0\xdc" ++ "\x00\xe0\x01\x7b", ++ .klen = 8 + 20 + 20, ++ .iv = "\x27\x77\x7f\x3f\x4a\x17\x86\xf0", ++ .assoc = "\x27\x77\x7f\x3f\x4a\x17\x86\xf0", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" ++ "\x20\x21\x22\x23", ++ .plen = 36, ++ .ctext = "\xc1\xcf\x48\xa8\x9f\x2f\xfd\xd9" ++ "\xcf\x46\x52\xe9\xef\xdb\x72\xd7" ++ "\x45\x40\xa4\x2b\xde\x6d\x78\x36" ++ "\xd5\x9a\x5c\xea\xae\xf3\x10\x53" ++ "\x25\xb2\x07\x2f" ++ "\x2c\x86\xa0\x90\x8e\xc1\x02\x1d" ++ "\x51\xdc\xd6\x21\xc7\x30\xcc\x32" ++ "\x38\x55\x47\x64", ++ .clen = 36 + 20, ++ }, { /* RFC 3686 Case 4 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x1c" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00" ++ "\x16\xaf\x5b\x14\x5f\xc9\xf5\x79" ++ "\xc1\x75\xf9\x3e\x3b\xfb\x0e\xed" ++ "\x86\x3d\x06\xcc\xfd\xb7\x85\x15" ++ "\x00\x00\x00\x48", ++ .klen = 8 + 20 + 28, ++ .iv = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", ++ .assoc = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\x4b\x55\x38\x4f\xe2\x59\xc9\xc8" ++ "\x4e\x79\x35\xa0\x03\xcb\xe9\x28" ++ "\xe9\x4e\x49\xf0\x6b\x8d\x58\x2b" ++ "\x26\x7f\xf3\xab\xeb\x2f\x74\x2f" ++ "\x45\x43\x64\xc1", ++ .clen = 16 + 20, ++ }, { /* RFC 3686 Case 5 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x1c" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33" ++ "\x7c\x5c\xb2\x40\x1b\x3d\xc3\x3c" ++ "\x19\xe7\x34\x08\x19\xe0\xf6\x9c" ++ "\x67\x8c\x3d\xb8\xe6\xf6\xa9\x1a" ++ "\x00\x96\xb0\x3b", ++ .klen = 8 + 20 + 28, ++ .iv = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", ++ .assoc = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\x45\x32\x43\xfc\x60\x9b\x23\x32" ++ "\x7e\xdf\xaa\xfa\x71\x31\xcd\x9f" ++ "\x84\x90\x70\x1c\x5a\xd4\xa7\x9c" ++ "\xfc\x1f\xe0\xff\x42\xf4\xfb\x00" ++ "\xab\xc4\xfa\x6d\x20\xe1\xce\x72" ++ "\x0e\x92\x4e\x97\xaa\x4d\x30\x84" ++ "\xb6\xd8\x4d\x3b", ++ .clen = 32 + 20, ++ }, { /* RFC 3686 Case 7 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x24" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00" ++ "\x77\x6b\xef\xf2\x85\x1d\xb0\x6f" ++ "\x4c\x8a\x05\x42\xc8\x69\x6f\x6c" ++ "\x6a\x81\xaf\x1e\xec\x96\xb4\xd3" ++ "\x7f\xc1\xd6\x89\xe6\xc1\xc1\x04" ++ "\x00\x00\x00\x60", ++ .klen = 8 + 20 + 36, ++ .iv = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", ++ .assoc = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\x14\x5a\xd0\x1d\xbf\x82\x4e\xc7" ++ "\x56\x08\x63\xdc\x71\xe3\xe0\xc0" ++ "\x3d\x6c\x23\x27\xda\x0e\x7f\x29" ++ "\xfd\x8d\x3c\x1b\xf7\x7a\x63\xd9" ++ "\x7e\x0f\xe9\xf6", ++ .clen = 16 + 20, ++ }, { /* RFC 3686 Case 8 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x24" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33" ++ "\xf6\xd6\x6d\x6b\xd5\x2d\x59\xbb" ++ "\x07\x96\x36\x58\x79\xef\xf8\x86" ++ "\xc6\x6d\xd5\x1a\x5b\x6a\x99\x74" ++ "\x4b\x50\x59\x0c\x87\xa2\x38\x84" ++ "\x00\xfa\xac\x24", ++ .klen = 8 + 20 + 36, ++ .iv = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", ++ .assoc = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\xf0\x5e\x23\x1b\x38\x94\x61\x2c" ++ "\x49\xee\x00\x0b\x80\x4e\xb2\xa9" ++ "\xb8\x30\x6b\x50\x8f\x83\x9d\x6a" ++ "\x55\x30\x83\x1d\x93\x44\xaf\x1c" ++ "\xe7\xee\x22\xa4\xdd\xbf\x5d\x44" ++ "\x3b\x43\x1c\x69\x55\x11\xd5\xad" ++ "\x14\x5f\x44\xa6", ++ .clen = 32 + 20, ++ }, ++}; ++ + static const struct aead_testvec hmac_sha1_ecb_cipher_null_tv_temp[] = { + { /* Input data from RFC 2410 Case 1 */ + #ifdef __LITTLE_ENDIAN diff --git a/target/linux/generic/backport-6.12/916-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch b/target/linux/generic/backport-6.12/916-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch new file mode 100644 index 00000000000..6999c919516 --- /dev/null +++ b/target/linux/generic/backport-6.12/916-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch @@ -0,0 +1,278 @@ +From 3f4d32aff1d4e06501f909b1e5037ec8caa76d01 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Thu, 5 Mar 2026 21:08:21 +0100 +Subject: [PATCH 2/5] crypto: testmgr - Add test vectors for + authenc(hmac(sha224),rfc3686(ctr(aes))) + +Test vectors were generated starting from existing RFC3686(CTR(AES)) test +vectors and adding HMAC(SHA224) computed with software implementation. +Then, the results were double-checked on Mediatek MT7986 (safexcel). +Platform pass self-tests. + +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + crypto/testmgr.c | 6 +- + crypto/testmgr.h | 235 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 240 insertions(+), 1 deletion(-) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4452,8 +4452,12 @@ static const struct alg_test_desc alg_te + } + }, { + .alg = "authenc(hmac(sha224),rfc3686(ctr(aes)))", +- .test = alg_test_null, ++ .generic_driver = "authenc(hmac-sha224-lib,rfc3686(ctr(aes-lib)))", ++ .test = alg_test_aead, + .fips_allowed = 1, ++ .suite = { ++ .aead = __VECS(hmac_sha224_aes_ctr_rfc3686_tv_temp) ++ } + }, { + .alg = "authenc(hmac(sha256),cbc(aes))", + .test = alg_test_aead, +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -17996,6 +17996,241 @@ static const struct aead_testvec hmac_sh + }, + }; + ++static const struct aead_testvec hmac_sha224_aes_ctr_rfc3686_tv_temp[] = { ++ { /* RFC 3686 Case 1 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00" ++ "\xae\x68\x52\xf8\x12\x10\x67\xcc" ++ "\x4b\xf7\xa5\x76\x55\x77\xf3\x9e" ++ "\x00\x00\x00\x30", ++ .klen = 8 + 28 + 20, ++ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", ++ .assoc = "\x00\x00\x00\x00\x00\x00\x00\x00", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\xe4\x09\x5d\x4f\xb7\xa7\xb3\x79" ++ "\x2d\x61\x75\xa3\x26\x13\x11\xb8" ++ "\x36\xb4\x3b\x9c\x62\xed\xcf\x77" ++ "\xdc\x19\x27\x3f\x92\x80\x52\xce" ++ "\x8f\xad\x01\x0b\x79\xda\x04\x83" ++ "\xcb\x45\x1a\x52", ++ .clen = 16 + 28, ++ }, { /* RFC 3686 Case 2 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b" ++ "\x7e\x24\x06\x78\x17\xfa\xe0\xd7" ++ "\x43\xd6\xce\x1f\x32\x53\x91\x63" ++ "\x00\x6c\xb6\xdb", ++ .klen = 8 + 28 + 20, ++ .iv = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", ++ .assoc = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\x51\x04\xa1\x06\x16\x8a\x72\xd9" ++ "\x79\x0d\x41\xee\x8e\xda\xd3\x88" ++ "\xeb\x2e\x1e\xfc\x46\xda\x57\xc8" ++ "\xfc\xe6\x30\xdf\x91\x41\xbe\x28" ++ "\x7f\xe4\x8f\xa7\x06\x71\xe9\xe5" ++ "\x16\x79\xef\xf9\x7e\x5c\x93\x4d" ++ "\xa0\xf8\x3b\x3a\xaa\x1c\xc0\xd9" ++ "\x6b\x48\x49\x01", ++ .clen = 32 + 28, ++ }, { /* RFC 3686 Case 3 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd" ++ "\x76\x91\xbe\x03\x5e\x50\x20\xa8" ++ "\xac\x6e\x61\x85\x29\xf9\xa0\xdc" ++ "\x00\xe0\x01\x7b", ++ .klen = 8 + 28 + 20, ++ .iv = "\x27\x77\x7f\x3f\x4a\x17\x86\xf0", ++ .assoc = "\x27\x77\x7f\x3f\x4a\x17\x86\xf0", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" ++ "\x20\x21\x22\x23", ++ .plen = 36, ++ .ctext = "\xc1\xcf\x48\xa8\x9f\x2f\xfd\xd9" ++ "\xcf\x46\x52\xe9\xef\xdb\x72\xd7" ++ "\x45\x40\xa4\x2b\xde\x6d\x78\x36" ++ "\xd5\x9a\x5c\xea\xae\xf3\x10\x53" ++ "\x25\xb2\x07\x2f" ++ "\xb0\x19\x45\xee\xa7\x31\xd9\xd0" ++ "\x74\x6b\xb8\xb1\x67\x61\x2f\x8c" ++ "\x68\xde\xe3\xc9\x3b\x0c\x72\xda" ++ "\x48\xba\x1b\x51", ++ .clen = 36 + 28, ++ }, { /* RFC 3686 Case 4 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x1c" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00" ++ "\x16\xaf\x5b\x14\x5f\xc9\xf5\x79" ++ "\xc1\x75\xf9\x3e\x3b\xfb\x0e\xed" ++ "\x86\x3d\x06\xcc\xfd\xb7\x85\x15" ++ "\x00\x00\x00\x48", ++ .klen = 8 + 28 + 28, ++ .iv = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", ++ .assoc = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\x4b\x55\x38\x4f\xe2\x59\xc9\xc8" ++ "\x4e\x79\x35\xa0\x03\xcb\xe9\x28" ++ "\xfd\xf5\x35\x26\x50\x3d\xdf\x80" ++ "\x6e\xbe\xba\x8d\x56\xf3\x03\xb7" ++ "\x27\xb8\x13\xe8\x72\x8f\xc9\x52" ++ "\x4a\xb7\xc3\x3a", ++ .clen = 16 + 28, ++ }, { /* RFC 3686 Case 5 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x1c" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b" ++ "\x7c\x5c\xb2\x40\x1b\x3d\xc3\x3c" ++ "\x19\xe7\x34\x08\x19\xe0\xf6\x9c" ++ "\x67\x8c\x3d\xb8\xe6\xf6\xa9\x1a" ++ "\x00\x96\xb0\x3b", ++ .klen = 8 + 28 + 28, ++ .iv = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", ++ .assoc = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\x45\x32\x43\xfc\x60\x9b\x23\x32" ++ "\x7e\xdf\xaa\xfa\x71\x31\xcd\x9f" ++ "\x84\x90\x70\x1c\x5a\xd4\xa7\x9c" ++ "\xfc\x1f\xe0\xff\x42\xf4\xfb\x00" ++ "\x72\x89\xa8\x04\xa5\xac\x8f\x29" ++ "\xe6\xb8\x58\xe8\xcf\x6a\x91\x89" ++ "\xd3\x66\x3b\xdc\xce\x43\x23\xb7" ++ "\x6a\xdd\x9d\xbd", ++ .clen = 32 + 28, ++ }, { /* RFC 3686 Case 7 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x24" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00" ++ "\x77\x6b\xef\xf2\x85\x1d\xb0\x6f" ++ "\x4c\x8a\x05\x42\xc8\x69\x6f\x6c" ++ "\x6a\x81\xaf\x1e\xec\x96\xb4\xd3" ++ "\x7f\xc1\xd6\x89\xe6\xc1\xc1\x04" ++ "\x00\x00\x00\x60", ++ .klen = 8 + 28 + 36, ++ .iv = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", ++ .assoc = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\x14\x5a\xd0\x1d\xbf\x82\x4e\xc7" ++ "\x56\x08\x63\xdc\x71\xe3\xe0\xc0" ++ "\xfe\xdf\x6f\x62\x8a\x79\xb5\x34" ++ "\xd0\x6f\x32\xaf\x31\x50\x5b\x1f" ++ "\xe0\x6d\x0b\xbc\x02\x25\xee\x74" ++ "\x7a\xdf\x97\x3c", ++ .clen = 16 + 28, ++ }, { /* RFC 3686 Case 8 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x24" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b" ++ "\xf6\xd6\x6d\x6b\xd5\x2d\x59\xbb" ++ "\x07\x96\x36\x58\x79\xef\xf8\x86" ++ "\xc6\x6d\xd5\x1a\x5b\x6a\x99\x74" ++ "\x4b\x50\x59\x0c\x87\xa2\x38\x84" ++ "\x00\xfa\xac\x24", ++ .klen = 8 + 28 + 36, ++ .iv = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", ++ .assoc = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\xf0\x5e\x23\x1b\x38\x94\x61\x2c" ++ "\x49\xee\x00\x0b\x80\x4e\xb2\xa9" ++ "\xb8\x30\x6b\x50\x8f\x83\x9d\x6a" ++ "\x55\x30\x83\x1d\x93\x44\xaf\x1c" ++ "\x19\x1e\x9c\x2c\x6d\x4e\x21\xda" ++ "\x6c\x4d\x88\x90\xf8\x5f\xa5\x9d" ++ "\xb4\xd4\x40\xad\xfa\x67\x3f\x0e" ++ "\x11\x12\xd6\x10", ++ .clen = 32 + 28, ++ }, ++}; ++ + static const struct aead_testvec hmac_sha256_aes_cbc_tv_temp[] = { + { /* RFC 3602 Case 1 */ + #ifdef __LITTLE_ENDIAN diff --git a/target/linux/generic/backport-6.12/917-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch b/target/linux/generic/backport-6.12/917-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch new file mode 100644 index 00000000000..bd09c4cedf7 --- /dev/null +++ b/target/linux/generic/backport-6.12/917-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch @@ -0,0 +1,278 @@ +From 65af3a0bff7264ab84f9e1f2099c658601ca1b66 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Thu, 5 Mar 2026 21:08:22 +0100 +Subject: [PATCH 3/5] crypto: testmgr - Add test vectors for + authenc(hmac(sha256),rfc3686(ctr(aes))) + +Test vectors were generated starting from existing RFC3686(CTR(AES)) test +vectors and adding HMAC(SHA256) computed with software implementation. +Then, the results were double-checked on Mediatek MT7986 (safexcel). +Platform pass self-tests. + +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + crypto/testmgr.c | 6 +- + crypto/testmgr.h | 235 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 240 insertions(+), 1 deletion(-) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4483,8 +4483,12 @@ static const struct alg_test_desc alg_te + .fips_allowed = 1, + }, { + .alg = "authenc(hmac(sha256),rfc3686(ctr(aes)))", +- .test = alg_test_null, ++ .generic_driver = "authenc(hmac-sha256-lib,rfc3686(ctr(aes-lib)))", ++ .test = alg_test_aead, + .fips_allowed = 1, ++ .suite = { ++ .aead = __VECS(hmac_sha256_aes_ctr_rfc3686_tv_temp) ++ } + }, { + .alg = "authenc(hmac(sha384),cbc(aes))", + .generic_driver = "authenc(hmac-sha384-lib,cbc(aes-generic))", +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -18514,6 +18514,241 @@ static const struct aead_testvec hmac_sh + }, + }; + ++static const struct aead_testvec hmac_sha256_aes_ctr_rfc3686_tv_temp[] = { ++ { /* RFC 3686 Case 1 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\xae\x68\x52\xf8\x12\x10\x67\xcc" ++ "\x4b\xf7\xa5\x76\x55\x77\xf3\x9e" ++ "\x00\x00\x00\x30", ++ .klen = 8 + 32 + 20, ++ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", ++ .assoc = "\x00\x00\x00\x00\x00\x00\x00\x00", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\xe4\x09\x5d\x4f\xb7\xa7\xb3\x79" ++ "\x2d\x61\x75\xa3\x26\x13\x11\xb8" ++ "\x9b\xa2\x34\x62\xe5\xb3\xe8\x2d" ++ "\x6d\xdb\x93\x64\xa5\x08\x2e\x77" ++ "\x72\x1f\x21\x94\xc7\xbe\x14\xa6" ++ "\xcd\xea\x96\xa1\x29\x8f\x30\xc3", ++ .clen = 16 + 32, ++ }, { /* RFC 3686 Case 2 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ++ "\x7e\x24\x06\x78\x17\xfa\xe0\xd7" ++ "\x43\xd6\xce\x1f\x32\x53\x91\x63" ++ "\x00\x6c\xb6\xdb", ++ .klen = 8 + 32 + 20, ++ .iv = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", ++ .assoc = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\x51\x04\xa1\x06\x16\x8a\x72\xd9" ++ "\x79\x0d\x41\xee\x8e\xda\xd3\x88" ++ "\xeb\x2e\x1e\xfc\x46\xda\x57\xc8" ++ "\xfc\xe6\x30\xdf\x91\x41\xbe\x28" ++ "\x22\xf7\x95\xa8\xbb\xcd\x19\xf4" ++ "\x58\x16\x54\x28\x2b\xf4\x52\xe7" ++ "\x5c\x6c\xe1\x44\x0b\xd5\x10\x6e" ++ "\xe1\xf7\x04\xc4\x2c\xab\x93\xdd", ++ .clen = 32 + 32, ++ }, { /* RFC 3686 Case 3 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd\xee\xff\x11\x22" ++ "\x76\x91\xbe\x03\x5e\x50\x20\xa8" ++ "\xac\x6e\x61\x85\x29\xf9\xa0\xdc" ++ "\x00\xe0\x01\x7b", ++ .klen = 8 + 32 + 20, ++ .iv = "\x27\x77\x7f\x3f\x4a\x17\x86\xf0", ++ .assoc = "\x27\x77\x7f\x3f\x4a\x17\x86\xf0", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" ++ "\x20\x21\x22\x23", ++ .plen = 36, ++ .ctext = "\xc1\xcf\x48\xa8\x9f\x2f\xfd\xd9" ++ "\xcf\x46\x52\xe9\xef\xdb\x72\xd7" ++ "\x45\x40\xa4\x2b\xde\x6d\x78\x36" ++ "\xd5\x9a\x5c\xea\xae\xf3\x10\x53" ++ "\x25\xb2\x07\x2f" ++ "\x1d\x05\x5f\x77\x3b\x4f\x5c\x21" ++ "\x29\xea\xf1\xa8\x71\x49\x7b\x0b" ++ "\x66\x0d\xff\x18\x81\x63\xfc\xc3" ++ "\x91\xb6\x38\xc8\xcd\x2d\x39\x83", ++ .clen = 36 + 32, ++ }, { /* RFC 3686 Case 4 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x1c" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x16\xaf\x5b\x14\x5f\xc9\xf5\x79" ++ "\xc1\x75\xf9\x3e\x3b\xfb\x0e\xed" ++ "\x86\x3d\x06\xcc\xfd\xb7\x85\x15" ++ "\x00\x00\x00\x48", ++ .klen = 8 + 32 + 28, ++ .iv = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", ++ .assoc = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\x4b\x55\x38\x4f\xe2\x59\xc9\xc8" ++ "\x4e\x79\x35\xa0\x03\xcb\xe9\x28" ++ "\x8d\x03\x77\xb2\x1c\xc9\xe0\xac" ++ "\xde\x69\xbe\x8a\xef\x5b\x13\x74" ++ "\x1d\x39\xbc\xdc\x95\xa4\xbf\xc3" ++ "\xd5\xc6\xd1\xda\xda\x3b\xca\x78", ++ .clen = 16 + 32, ++ }, { /* RFC 3686 Case 5 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x1c" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ++ "\x7c\x5c\xb2\x40\x1b\x3d\xc3\x3c" ++ "\x19\xe7\x34\x08\x19\xe0\xf6\x9c" ++ "\x67\x8c\x3d\xb8\xe6\xf6\xa9\x1a" ++ "\x00\x96\xb0\x3b", ++ .klen = 8 + 32 + 28, ++ .iv = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", ++ .assoc = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\x45\x32\x43\xfc\x60\x9b\x23\x32" ++ "\x7e\xdf\xaa\xfa\x71\x31\xcd\x9f" ++ "\x84\x90\x70\x1c\x5a\xd4\xa7\x9c" ++ "\xfc\x1f\xe0\xff\x42\xf4\xfb\x00" ++ "\x34\x06\x2b\x3d\xf1\xa8\x3d\xf1" ++ "\xa6\x5e\x5c\x1a\xdb\x0c\xb5\x1e" ++ "\x8f\xdb\xf4\xca\x7d\x09\x5e\x81" ++ "\xdb\x32\x07\x4a\x1d\x1c\x6d\x83", ++ .clen = 32 + 32, ++ }, { /* RFC 3686 Case 7 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x24" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x77\x6b\xef\xf2\x85\x1d\xb0\x6f" ++ "\x4c\x8a\x05\x42\xc8\x69\x6f\x6c" ++ "\x6a\x81\xaf\x1e\xec\x96\xb4\xd3" ++ "\x7f\xc1\xd6\x89\xe6\xc1\xc1\x04" ++ "\x00\x00\x00\x60", ++ .klen = 8 + 32 + 36, ++ .iv = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", ++ .assoc = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\x14\x5a\xd0\x1d\xbf\x82\x4e\xc7" ++ "\x56\x08\x63\xdc\x71\xe3\xe0\xc0" ++ "\xc3\xb4\x5f\xb0\xbf\xf5\x1b\xff" ++ "\x7c\xf1\x79\x00\x63\x50\xdd\x77" ++ "\xc0\x4a\xba\xcd\xdc\x47\x05\x2a" ++ "\x5d\x85\x2d\x83\x44\xca\x79\x2c", ++ .clen = 16 + 32, ++ }, { /* RFC 3686 Case 8 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x24" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ++ "\xf6\xd6\x6d\x6b\xd5\x2d\x59\xbb" ++ "\x07\x96\x36\x58\x79\xef\xf8\x86" ++ "\xc6\x6d\xd5\x1a\x5b\x6a\x99\x74" ++ "\x4b\x50\x59\x0c\x87\xa2\x38\x84" ++ "\x00\xfa\xac\x24", ++ .klen = 8 + 32 + 36, ++ .iv = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", ++ .assoc = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\xf0\x5e\x23\x1b\x38\x94\x61\x2c" ++ "\x49\xee\x00\x0b\x80\x4e\xb2\xa9" ++ "\xb8\x30\x6b\x50\x8f\x83\x9d\x6a" ++ "\x55\x30\x83\x1d\x93\x44\xaf\x1c" ++ "\xc8\x59\x5d\xe1\xba\xac\x13\x82" ++ "\xfd\x21\x7c\x8c\x23\x31\x04\x02" ++ "\x9e\x69\x5b\x57\xa8\x13\xe7\x21" ++ "\x60\x0c\x24\xc2\x80\x4a\x93\x6e", ++ .clen = 32 + 32, ++ }, ++}; ++ + static const struct aead_testvec hmac_sha384_aes_cbc_tv_temp[] = { + { /* RFC 3602 Case 1 */ + #ifdef __LITTLE_ENDIAN diff --git a/target/linux/generic/backport-6.12/918-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch b/target/linux/generic/backport-6.12/918-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch new file mode 100644 index 00000000000..5283fcc028c --- /dev/null +++ b/target/linux/generic/backport-6.12/918-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch @@ -0,0 +1,306 @@ +From 07cbbf6f38e6a91a7fecab3d6f86485425748d3e Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Thu, 5 Mar 2026 21:08:23 +0100 +Subject: [PATCH 4/5] crypto: testmgr - Add test vectors for + authenc(hmac(sha384),rfc3686(ctr(aes))) + +Test vectors were generated starting from existing RFC3686(CTR(AES)) test +vectors and adding HMAC(SHA384) computed with software implementation. +Then, the results were double-checked on Mediatek MT7986 (safexcel). +Platform pass self-tests. + +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + crypto/testmgr.c | 6 +- + crypto/testmgr.h | 263 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 268 insertions(+), 1 deletion(-) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4514,8 +4514,12 @@ static const struct alg_test_desc alg_te + .fips_allowed = 1, + }, { + .alg = "authenc(hmac(sha384),rfc3686(ctr(aes)))", +- .test = alg_test_null, ++ .generic_driver = "authenc(hmac-sha384-lib,rfc3686(ctr(aes-lib)))", ++ .test = alg_test_aead, + .fips_allowed = 1, ++ .suite = { ++ .aead = __VECS(hmac_sha384_aes_ctr_rfc3686_tv_temp) ++ } + }, { + .alg = "authenc(hmac(sha512),cbc(aes))", + .fips_allowed = 1, +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -19060,6 +19060,269 @@ static const struct aead_testvec hmac_sh + }, + }; + ++static const struct aead_testvec hmac_sha384_aes_ctr_rfc3686_tv_temp[] = { ++ { /* RFC 3686 Case 1 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\xae\x68\x52\xf8\x12\x10\x67\xcc" ++ "\x4b\xf7\xa5\x76\x55\x77\xf3\x9e" ++ "\x00\x00\x00\x30", ++ .klen = 8 + 48 + 20, ++ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", ++ .assoc = "\x00\x00\x00\x00\x00\x00\x00\x00", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\xe4\x09\x5d\x4f\xb7\xa7\xb3\x79" ++ "\x2d\x61\x75\xa3\x26\x13\x11\xb8" ++ "\x45\x51\x59\x72\x16\xd3\xc6\x15" ++ "\x25\x1e\xe8\x92\x2e\x47\x52\xcc" ++ "\x91\x9c\x24\xef\x11\xb2\x53\x00" ++ "\x10\x20\x43\x06\xe2\x35\x88\x9e" ++ "\x18\x32\x5a\x79\x7d\x73\x7e\x89" ++ "\xfe\xa1\xda\xa4\x86\xc4\x2a\x04", ++ .clen = 16 + 48, ++ }, { /* RFC 3686 Case 2 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ++ "\x40\x41\x42\x43\x44\x45\x46\x47" ++ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" ++ "\x7e\x24\x06\x78\x17\xfa\xe0\xd7" ++ "\x43\xd6\xce\x1f\x32\x53\x91\x63" ++ "\x00\x6c\xb6\xdb", ++ .klen = 8 + 48 + 20, ++ .iv = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", ++ .assoc = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\x51\x04\xa1\x06\x16\x8a\x72\xd9" ++ "\x79\x0d\x41\xee\x8e\xda\xd3\x88" ++ "\xeb\x2e\x1e\xfc\x46\xda\x57\xc8" ++ "\xfc\xe6\x30\xdf\x91\x41\xbe\x28" ++ "\x83\x65\x32\x1e\x6b\x60\xe6\x4a" ++ "\xe2\xab\x52\x2b\xa6\x70\x3a\xfa" ++ "\xd2\xec\x83\xe4\x31\x0c\x28\x40" ++ "\x9b\x5e\x18\xa4\xdc\x48\xb8\x56" ++ "\x33\xab\x7f\x2b\xaf\xe4\x3a\xe3" ++ "\x8a\x61\xf6\x22\xb4\x6b\xfe\x7d", ++ .clen = 32 + 48, ++ }, { /* RFC 3686 Case 3 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd\xee\xff\x11\x22" ++ "\x33\x44\x55\x66\x77\x88\x99\xaa" ++ "\xbb\xcc\xdd\xee\xff\x11\x22\x33" ++ "\x76\x91\xbe\x03\x5e\x50\x20\xa8" ++ "\xac\x6e\x61\x85\x29\xf9\xa0\xdc" ++ "\x00\xe0\x01\x7b", ++ .klen = 8 + 48 + 20, ++ .iv = "\x27\x77\x7f\x3f\x4a\x17\x86\xf0", ++ .assoc = "\x27\x77\x7f\x3f\x4a\x17\x86\xf0", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" ++ "\x20\x21\x22\x23", ++ .plen = 36, ++ .ctext = "\xc1\xcf\x48\xa8\x9f\x2f\xfd\xd9" ++ "\xcf\x46\x52\xe9\xef\xdb\x72\xd7" ++ "\x45\x40\xa4\x2b\xde\x6d\x78\x36" ++ "\xd5\x9a\x5c\xea\xae\xf3\x10\x53" ++ "\x25\xb2\x07\x2f" ++ "\x4a\xaa\xad\x3b\x3b\xb6\x9a\xba" ++ "\xa1\x7b\xc6\xce\x96\xc3\xff\x67" ++ "\xf3\x0c\x33\x57\xf0\x51\x24\x08" ++ "\xed\x4f\x6a\x9c\x22\x42\xbd\x18" ++ "\x97\x74\x68\x36\x00\xf1\x69\x3a" ++ "\x18\x77\x40\xf0\x56\xba\xba\xe0", ++ .clen = 36 + 48, ++ }, { /* RFC 3686 Case 4 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x1c" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x16\xaf\x5b\x14\x5f\xc9\xf5\x79" ++ "\xc1\x75\xf9\x3e\x3b\xfb\x0e\xed" ++ "\x86\x3d\x06\xcc\xfd\xb7\x85\x15" ++ "\x00\x00\x00\x48", ++ .klen = 8 + 48 + 28, ++ .iv = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", ++ .assoc = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\x4b\x55\x38\x4f\xe2\x59\xc9\xc8" ++ "\x4e\x79\x35\xa0\x03\xcb\xe9\x28" ++ "\x36\xd6\xc7\x55\xac\xb6\x0b\x14" ++ "\x95\x71\xf9\x86\x30\xe3\x96\xc3" ++ "\x76\x85\x6d\xa5\x06\xed\x6f\x34" ++ "\xcc\x1f\xcc\x2d\x88\x06\xb0\x1d" ++ "\xbe\xd9\xa2\xd3\x64\xf1\x33\x03" ++ "\x13\x50\x8f\xae\x61\x2d\x82\xb8", ++ .clen = 16 + 48, ++ }, { /* RFC 3686 Case 5 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x1c" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ++ "\x40\x41\x42\x43\x44\x45\x46\x47" ++ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" ++ "\x7c\x5c\xb2\x40\x1b\x3d\xc3\x3c" ++ "\x19\xe7\x34\x08\x19\xe0\xf6\x9c" ++ "\x67\x8c\x3d\xb8\xe6\xf6\xa9\x1a" ++ "\x00\x96\xb0\x3b", ++ .klen = 8 + 48 + 28, ++ .iv = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", ++ .assoc = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\x45\x32\x43\xfc\x60\x9b\x23\x32" ++ "\x7e\xdf\xaa\xfa\x71\x31\xcd\x9f" ++ "\x84\x90\x70\x1c\x5a\xd4\xa7\x9c" ++ "\xfc\x1f\xe0\xff\x42\xf4\xfb\x00" ++ "\x80\x12\x67\x22\xf2\x4d\x9b\xbf" ++ "\xdc\x38\xd3\xaa\x12\xc0\x58\x1a" ++ "\x9a\x62\x6e\x42\x3d\x44\x63\xdd" ++ "\xee\x7e\xe3\xa3\xdf\x2a\x65\x05" ++ "\xd0\xc1\xd2\x54\x55\x35\x5c\xc7" ++ "\xb0\xb5\xb1\x36\xe0\x0b\xaf\x72", ++ .clen = 32 + 48, ++ }, { /* RFC 3686 Case 7 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x24" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x77\x6b\xef\xf2\x85\x1d\xb0\x6f" ++ "\x4c\x8a\x05\x42\xc8\x69\x6f\x6c" ++ "\x6a\x81\xaf\x1e\xec\x96\xb4\xd3" ++ "\x7f\xc1\xd6\x89\xe6\xc1\xc1\x04" ++ "\x00\x00\x00\x60", ++ .klen = 8 + 48 + 36, ++ .iv = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", ++ .assoc = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\x14\x5a\xd0\x1d\xbf\x82\x4e\xc7" ++ "\x56\x08\x63\xdc\x71\xe3\xe0\xc0" ++ "\xb1\x7b\xb1\xec\xca\x94\x55\xc4" ++ "\x3f\x2b\xb1\x70\x04\x91\xf5\x9d" ++ "\x1a\xc0\xe1\x2a\x93\x5f\x96\x2a" ++ "\x12\x85\x38\x36\xe1\xb2\xe9\xf0" ++ "\xf2\x6e\x5d\x81\xcc\x49\x07\x9c" ++ "\x5b\x88\xc8\xcc\xc4\x21\x4f\x32", ++ .clen = 16 + 48, ++ }, { /* RFC 3686 Case 8 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x24" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ++ "\x40\x41\x42\x43\x44\x45\x46\x47" ++ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" ++ "\xf6\xd6\x6d\x6b\xd5\x2d\x59\xbb" ++ "\x07\x96\x36\x58\x79\xef\xf8\x86" ++ "\xc6\x6d\xd5\x1a\x5b\x6a\x99\x74" ++ "\x4b\x50\x59\x0c\x87\xa2\x38\x84" ++ "\x00\xfa\xac\x24", ++ .klen = 8 + 48 + 36, ++ .iv = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", ++ .assoc = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\xf0\x5e\x23\x1b\x38\x94\x61\x2c" ++ "\x49\xee\x00\x0b\x80\x4e\xb2\xa9" ++ "\xb8\x30\x6b\x50\x8f\x83\x9d\x6a" ++ "\x55\x30\x83\x1d\x93\x44\xaf\x1c" ++ "\xd6\x96\xbb\x12\x39\xc4\x4d\xe2" ++ "\x4c\x02\xe7\x1f\xdc\xb2\xb1\x57" ++ "\x38\x0d\xdd\x13\xb3\x89\x57\x9e" ++ "\x1f\xb5\x48\x32\xc4\xd3\x9d\x1f" ++ "\x68\xab\x8d\xc6\xa8\x05\x3a\xc2" ++ "\x87\xaf\x23\xb3\xe4\x1b\xde\xb3", ++ .clen = 32 + 48, ++ }, ++}; ++ + static const struct aead_testvec hmac_sha512_aes_cbc_tv_temp[] = { + { /* RFC 3602 Case 1 */ + #ifdef __LITTLE_ENDIAN diff --git a/target/linux/generic/backport-6.12/919-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch b/target/linux/generic/backport-6.12/919-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch new file mode 100644 index 00000000000..87949eb412f --- /dev/null +++ b/target/linux/generic/backport-6.12/919-v7.1-crypto-testmgr-Add-test-vectors-for-authenc-hmac-sha.patch @@ -0,0 +1,334 @@ +From 07f1bc80c478c7198065d3896ed3685428314a9c Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Thu, 5 Mar 2026 21:08:24 +0100 +Subject: [PATCH 5/5] crypto: testmgr - Add test vectors for + authenc(hmac(sha512),rfc3686(ctr(aes))) + +Test vectors were generated starting from existing RFC3686(CTR(AES)) test +vectors and adding HMAC(SHA512) computed with software implementation. +Then, the results were double-checked on Mediatek MT7986 (safexcel). +Platform pass self-tests. + +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + crypto/testmgr.c | 6 +- + crypto/testmgr.h | 291 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 296 insertions(+), 1 deletion(-) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4545,8 +4545,12 @@ static const struct alg_test_desc alg_te + .fips_allowed = 1, + }, { + .alg = "authenc(hmac(sha512),rfc3686(ctr(aes)))", +- .test = alg_test_null, ++ .generic_driver = "authenc(hmac-sha512-lib,rfc3686(ctr(aes-lib)))", ++ .test = alg_test_aead, + .fips_allowed = 1, ++ .suite = { ++ .aead = __VECS(hmac_sha512_aes_ctr_rfc3686_tv_temp) ++ } + }, { + .alg = "blake2b-160", + .test = alg_test_hash, +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -19719,6 +19719,297 @@ static const struct aead_testvec hmac_md + }, + }; + ++static const struct aead_testvec hmac_sha512_aes_ctr_rfc3686_tv_temp[] = { ++ { /* RFC 3686 Case 1 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\xae\x68\x52\xf8\x12\x10\x67\xcc" ++ "\x4b\xf7\xa5\x76\x55\x77\xf3\x9e" ++ "\x00\x00\x00\x30", ++ .klen = 8 + 64 + 20, ++ .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", ++ .assoc = "\x00\x00\x00\x00\x00\x00\x00\x00", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\xe4\x09\x5d\x4f\xb7\xa7\xb3\x79" ++ "\x2d\x61\x75\xa3\x26\x13\x11\xb8" ++ "\xa4\x45\x3a\x44\x9c\xe5\x1c\xd9" ++ "\x10\x43\x51\x2e\x76\x5e\xf8\x9d" ++ "\x03\x12\x1a\x31\x00\x33\x10\xb4" ++ "\x94\x4b\x70\x84\x6c\xda\xb1\x46" ++ "\x24\xb6\x3b\x2a\xec\xd5\x67\xb8" ++ "\x65\xa2\xbd\xac\x18\xe2\xf8\x55" ++ "\xc6\x91\xb0\x92\x84\x2d\x74\x44" ++ "\xa7\xee\xc3\x44\xa0\x07\x0e\x62", ++ .clen = 16 + 64, ++ }, { /* RFC 3686 Case 2 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ++ "\x40\x41\x42\x43\x44\x45\x46\x47" ++ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" ++ "\x50\x51\x52\x53\x54\x55\x56\x57" ++ "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" ++ "\x7e\x24\x06\x78\x17\xfa\xe0\xd7" ++ "\x43\xd6\xce\x1f\x32\x53\x91\x63" ++ "\x00\x6c\xb6\xdb", ++ .klen = 8 + 64 + 20, ++ .iv = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", ++ .assoc = "\xc0\x54\x3b\x59\xda\x48\xd9\x0b", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\x51\x04\xa1\x06\x16\x8a\x72\xd9" ++ "\x79\x0d\x41\xee\x8e\xda\xd3\x88" ++ "\xeb\x2e\x1e\xfc\x46\xda\x57\xc8" ++ "\xfc\xe6\x30\xdf\x91\x41\xbe\x28" ++ "\xec\x67\x0d\xb3\xbd\x98\x13\x01" ++ "\x2b\x04\x9b\xe6\x06\x67\x3c\x76" ++ "\xcd\x41\xb7\xcc\x70\x6c\x7f\xc8" ++ "\x67\xbd\x22\x39\xb2\xaa\xe8\x88" ++ "\xe0\x4f\x81\x52\xdf\xc9\xc3\xd6" ++ "\x44\xf4\x66\x33\x87\x64\x61\x02" ++ "\x02\xa2\x64\x15\x2b\xe9\x0b\x3d" ++ "\x4c\xea\xa1\xa5\xa7\xc9\xd3\x1b", ++ .clen = 32 + 64, ++ }, { /* RFC 3686 Case 3 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x14" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x22\x33\x44\x55\x66\x77\x88\x99" ++ "\xaa\xbb\xcc\xdd\xee\xff\x11\x22" ++ "\x33\x44\x55\x66\x77\x88\x99\xaa" ++ "\xbb\xcc\xdd\xee\xff\x11\x22\x33" ++ "\x44\x55\x66\x77\x88\x99\xaa\xbb" ++ "\xcc\xdd\xee\xff\x11\x22\x33\x44" ++ "\x76\x91\xbe\x03\x5e\x50\x20\xa8" ++ "\xac\x6e\x61\x85\x29\xf9\xa0\xdc" ++ "\x00\xe0\x01\x7b", ++ .klen = 8 + 64 + 20, ++ .iv = "\x27\x77\x7f\x3f\x4a\x17\x86\xf0", ++ .assoc = "\x27\x77\x7f\x3f\x4a\x17\x86\xf0", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" ++ "\x20\x21\x22\x23", ++ .plen = 36, ++ .ctext = "\xc1\xcf\x48\xa8\x9f\x2f\xfd\xd9" ++ "\xcf\x46\x52\xe9\xef\xdb\x72\xd7" ++ "\x45\x40\xa4\x2b\xde\x6d\x78\x36" ++ "\xd5\x9a\x5c\xea\xae\xf3\x10\x53" ++ "\x25\xb2\x07\x2f" ++ "\x6f\x90\xb6\xa3\x35\x43\x59\xff" ++ "\x1e\x32\xd6\xfe\xfa\x33\xf9\xf0" ++ "\x31\x2f\x03\x2d\x88\x1d\xab\xbf" ++ "\x0e\x19\x16\xd9\xf3\x98\x3e\xdd" ++ "\x0c\xec\xfe\xe8\x89\x13\x91\x15" ++ "\xf6\x61\x65\x5c\x1b\x7d\xde\xc0" ++ "\xe4\xba\x6d\x27\xe2\x89\x23\x24" ++ "\x15\x82\x37\x3d\x48\xd3\xc9\x32", ++ .clen = 36 + 64, ++ }, { /* RFC 3686 Case 4 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x1c" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x16\xaf\x5b\x14\x5f\xc9\xf5\x79" ++ "\xc1\x75\xf9\x3e\x3b\xfb\x0e\xed" ++ "\x86\x3d\x06\xcc\xfd\xb7\x85\x15" ++ "\x00\x00\x00\x48", ++ .klen = 8 + 64 + 28, ++ .iv = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", ++ .assoc = "\x36\x73\x3c\x14\x7d\x6d\x93\xcb", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\x4b\x55\x38\x4f\xe2\x59\xc9\xc8" ++ "\x4e\x79\x35\xa0\x03\xcb\xe9\x28" ++ "\x25\xea\xdc\xad\x52\xb8\x0f\x70" ++ "\xe7\x39\x83\x80\x10\x3f\x18\xc4" ++ "\xf8\x59\x14\x25\x5f\xba\x20\x87" ++ "\x0b\x04\x5e\xf7\xde\x41\x39\xff" ++ "\xa2\xee\x84\x3f\x9d\x38\xfd\x17" ++ "\xc0\x66\x5e\x74\x39\xe3\xd3\xd7" ++ "\x3d\xbc\xe3\x99\x2f\xe7\xef\x37" ++ "\x61\x03\xf3\x9e\x01\xaf\xba\x9d", ++ .clen = 16 + 64, ++ }, { /* RFC 3686 Case 5 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x1c" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ++ "\x40\x41\x42\x43\x44\x45\x46\x47" ++ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" ++ "\x50\x51\x52\x53\x54\x55\x56\x57" ++ "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" ++ "\x7c\x5c\xb2\x40\x1b\x3d\xc3\x3c" ++ "\x19\xe7\x34\x08\x19\xe0\xf6\x9c" ++ "\x67\x8c\x3d\xb8\xe6\xf6\xa9\x1a" ++ "\x00\x96\xb0\x3b", ++ .klen = 8 + 64 + 28, ++ .iv = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", ++ .assoc = "\x02\x0c\x6e\xad\xc2\xcb\x50\x0d", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\x45\x32\x43\xfc\x60\x9b\x23\x32" ++ "\x7e\xdf\xaa\xfa\x71\x31\xcd\x9f" ++ "\x84\x90\x70\x1c\x5a\xd4\xa7\x9c" ++ "\xfc\x1f\xe0\xff\x42\xf4\xfb\x00" ++ "\x51\xa3\xe6\x1d\x23\x7d\xd1\x18" ++ "\x55\x9c\x1c\x92\x2b\xc2\xcd\xfe" ++ "\x8a\xa8\xa5\x96\x65\x2e\x9d\xdb" ++ "\x06\xd2\x1c\x57\x2b\x76\xb5\x9c" ++ "\xd4\x3e\x8b\x61\x54\x2d\x08\xe5" ++ "\xb2\xf8\x88\x20\x0c\xad\xe8\x85" ++ "\x61\x8e\x5c\xa4\x96\x2c\xe2\x7d" ++ "\x4f\xb6\x1d\xb2\x8c\xd7\xe3\x38", ++ .clen = 32 + 64, ++ }, { /* RFC 3686 Case 7 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x24" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x77\x6b\xef\xf2\x85\x1d\xb0\x6f" ++ "\x4c\x8a\x05\x42\xc8\x69\x6f\x6c" ++ "\x6a\x81\xaf\x1e\xec\x96\xb4\xd3" ++ "\x7f\xc1\xd6\x89\xe6\xc1\xc1\x04" ++ "\x00\x00\x00\x60", ++ .klen = 8 + 64 + 36, ++ .iv = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", ++ .assoc = "\xdb\x56\x72\xc9\x7a\xa8\xf0\xb2", ++ .alen = 8, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\x14\x5a\xd0\x1d\xbf\x82\x4e\xc7" ++ "\x56\x08\x63\xdc\x71\xe3\xe0\xc0" ++ "\x6b\x68\x0b\x99\x9a\x4d\xc8\xb9" ++ "\x35\xea\xcd\x56\x3f\x40\xa2\xb6" ++ "\x68\xda\x59\xd8\xa0\x89\xcd\x52" ++ "\xb1\x6e\xed\xc1\x42\x10\xa5\x0f" ++ "\x88\x0b\x80\xce\xc4\x67\xf0\x45" ++ "\x5d\xb2\x9e\xde\x1c\x79\x52\x0d" ++ "\xff\x75\x36\xd5\x0f\x52\x8e\xe5" ++ "\x31\x85\xcf\x1d\x31\xf8\x62\x67", ++ .clen = 16 + 64, ++ }, { /* RFC 3686 Case 8 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x24" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" ++ "\x40\x41\x42\x43\x44\x45\x46\x47" ++ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" ++ "\x50\x51\x52\x53\x54\x55\x56\x57" ++ "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" ++ "\xf6\xd6\x6d\x6b\xd5\x2d\x59\xbb" ++ "\x07\x96\x36\x58\x79\xef\xf8\x86" ++ "\xc6\x6d\xd5\x1a\x5b\x6a\x99\x74" ++ "\x4b\x50\x59\x0c\x87\xa2\x38\x84" ++ "\x00\xfa\xac\x24", ++ .klen = 8 + 64 + 36, ++ .iv = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", ++ .assoc = "\xc1\x58\x5e\xf1\x5a\x43\xd8\x75", ++ .alen = 8, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\xf0\x5e\x23\x1b\x38\x94\x61\x2c" ++ "\x49\xee\x00\x0b\x80\x4e\xb2\xa9" ++ "\xb8\x30\x6b\x50\x8f\x83\x9d\x6a" ++ "\x55\x30\x83\x1d\x93\x44\xaf\x1c" ++ "\x9a\xac\x38\xbd\xf3\xcf\xd5\xd0" ++ "\x09\x07\xa6\xe1\x7f\xd6\x79\x98" ++ "\x4e\x90\x0e\xc0\x3d\xa0\xf2\x12" ++ "\x52\x79\x9c\x17\xff\xb9\xb8\xe3" ++ "\x2f\x31\xcb\xbd\x63\x70\x72\x7b" ++ "\x4e\x1e\xd1\xde\xb5\x6b\x7d\x54" ++ "\x68\x56\xdd\xe5\x53\xee\x29\xd2" ++ "\x85\xa1\x73\x61\x00\xa9\x26\x8f", ++ .clen = 32 + 64, ++ }, ++}; ++ + static const struct aead_testvec hmac_sha1_des_cbc_tv_temp[] = { + { /*Generated with cryptopp*/ + #ifdef __LITTLE_ENDIAN diff --git a/target/linux/generic/backport-6.12/930-v7.1-crypto-safexcel-Group-authenc-ciphersuites.patch b/target/linux/generic/backport-6.12/930-v7.1-crypto-safexcel-Group-authenc-ciphersuites.patch new file mode 100644 index 00000000000..b0b2745f1f6 --- /dev/null +++ b/target/linux/generic/backport-6.12/930-v7.1-crypto-safexcel-Group-authenc-ciphersuites.patch @@ -0,0 +1,64 @@ +From c75daa3730132c55dea7cc9c0f8818aea491fe0c Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Tue, 3 Feb 2026 19:21:51 +0100 +Subject: [PATCH 1/2] crypto: safexcel - Group authenc ciphersuites + +Move authenc(sha1,des) and authenc(sha1,3des) ciphersuites to appropriate +groups. No functional changes intended. + +Signed-off-by: Aleksander Jan Bajkowski +Acked-by: Antoine Tenart +Signed-off-by: Herbert Xu +--- + drivers/crypto/inside-secure/safexcel.c | 4 ++-- + drivers/crypto/inside-secure/safexcel.h | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/crypto/inside-secure/safexcel.c ++++ b/drivers/crypto/inside-secure/safexcel.c +@@ -1209,7 +1209,6 @@ static struct safexcel_alg_template *saf + &safexcel_alg_authenc_hmac_sha256_cbc_aes, + &safexcel_alg_authenc_hmac_sha384_cbc_aes, + &safexcel_alg_authenc_hmac_sha512_cbc_aes, +- &safexcel_alg_authenc_hmac_sha1_cbc_des3_ede, + &safexcel_alg_authenc_hmac_sha1_ctr_aes, + &safexcel_alg_authenc_hmac_sha224_ctr_aes, + &safexcel_alg_authenc_hmac_sha256_ctr_aes, +@@ -1242,11 +1241,12 @@ static struct safexcel_alg_template *saf + &safexcel_alg_hmac_sha3_256, + &safexcel_alg_hmac_sha3_384, + &safexcel_alg_hmac_sha3_512, +- &safexcel_alg_authenc_hmac_sha1_cbc_des, ++ &safexcel_alg_authenc_hmac_sha1_cbc_des3_ede, + &safexcel_alg_authenc_hmac_sha256_cbc_des3_ede, + &safexcel_alg_authenc_hmac_sha224_cbc_des3_ede, + &safexcel_alg_authenc_hmac_sha512_cbc_des3_ede, + &safexcel_alg_authenc_hmac_sha384_cbc_des3_ede, ++ &safexcel_alg_authenc_hmac_sha1_cbc_des, + &safexcel_alg_authenc_hmac_sha256_cbc_des, + &safexcel_alg_authenc_hmac_sha224_cbc_des, + &safexcel_alg_authenc_hmac_sha512_cbc_des, +--- a/drivers/crypto/inside-secure/safexcel.h ++++ b/drivers/crypto/inside-secure/safexcel.h +@@ -950,7 +950,6 @@ extern struct safexcel_alg_template safe + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha256_cbc_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha384_cbc_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha512_cbc_aes; +-extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha1_cbc_des3_ede; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha1_ctr_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha224_ctr_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha256_ctr_aes; +@@ -983,11 +982,12 @@ extern struct safexcel_alg_template safe + extern struct safexcel_alg_template safexcel_alg_hmac_sha3_256; + extern struct safexcel_alg_template safexcel_alg_hmac_sha3_384; + extern struct safexcel_alg_template safexcel_alg_hmac_sha3_512; +-extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha1_cbc_des; ++extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha1_cbc_des3_ede; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha256_cbc_des3_ede; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha224_cbc_des3_ede; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha512_cbc_des3_ede; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha384_cbc_des3_ede; ++extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha1_cbc_des; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha256_cbc_des; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha224_cbc_des; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha512_cbc_des; diff --git a/target/linux/generic/backport-6.12/931-v7.1-crypto-safexcel-Add-support-for-authenc-hmac-md5-sui.patch b/target/linux/generic/backport-6.12/931-v7.1-crypto-safexcel-Add-support-for-authenc-hmac-md5-sui.patch new file mode 100644 index 00000000000..1a1616db721 --- /dev/null +++ b/target/linux/generic/backport-6.12/931-v7.1-crypto-safexcel-Add-support-for-authenc-hmac-md5-sui.patch @@ -0,0 +1,279 @@ +From f050e4209ab0ba3f13bb6272a07ce87cbea922c9 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Tue, 3 Feb 2026 19:21:52 +0100 +Subject: [PATCH 2/2] crypto: safexcel - Add support for authenc(hmac(md5),*) + suites + +This patch adds support for the following AEAD ciphersuites: +- authenc(hmac(md5),cbc(aes)) +- authenc(hmac(md5),cbc(des))) +- authenc(hmac(md5),cbc(des3_ede)) +- authenc(hmac(md5),rfc3686(ctr(aes))) + +The first three ciphersuites were tested using testmgr and the recently +sent test vectors. They passed self-tests. + +This is enhanced version of the patch found in the mtk-openwrt-feeds repo. + +Signed-off-by: Aleksander Jan Bajkowski +Reviewed-by: Antoine Tenart +Signed-off-by: Herbert Xu +--- + drivers/crypto/inside-secure/safexcel.c | 4 + + drivers/crypto/inside-secure/safexcel.h | 4 + + .../crypto/inside-secure/safexcel_cipher.c | 149 ++++++++++++++++++ + 3 files changed, 157 insertions(+) + +--- a/drivers/crypto/inside-secure/safexcel.c ++++ b/drivers/crypto/inside-secure/safexcel.c +@@ -1204,11 +1204,13 @@ static struct safexcel_alg_template *saf + &safexcel_alg_hmac_sha256, + &safexcel_alg_hmac_sha384, + &safexcel_alg_hmac_sha512, ++ &safexcel_alg_authenc_hmac_md5_cbc_aes, + &safexcel_alg_authenc_hmac_sha1_cbc_aes, + &safexcel_alg_authenc_hmac_sha224_cbc_aes, + &safexcel_alg_authenc_hmac_sha256_cbc_aes, + &safexcel_alg_authenc_hmac_sha384_cbc_aes, + &safexcel_alg_authenc_hmac_sha512_cbc_aes, ++ &safexcel_alg_authenc_hmac_md5_ctr_aes, + &safexcel_alg_authenc_hmac_sha1_ctr_aes, + &safexcel_alg_authenc_hmac_sha224_ctr_aes, + &safexcel_alg_authenc_hmac_sha256_ctr_aes, +@@ -1241,11 +1243,13 @@ static struct safexcel_alg_template *saf + &safexcel_alg_hmac_sha3_256, + &safexcel_alg_hmac_sha3_384, + &safexcel_alg_hmac_sha3_512, ++ &safexcel_alg_authenc_hmac_md5_cbc_des3_ede, + &safexcel_alg_authenc_hmac_sha1_cbc_des3_ede, + &safexcel_alg_authenc_hmac_sha256_cbc_des3_ede, + &safexcel_alg_authenc_hmac_sha224_cbc_des3_ede, + &safexcel_alg_authenc_hmac_sha512_cbc_des3_ede, + &safexcel_alg_authenc_hmac_sha384_cbc_des3_ede, ++ &safexcel_alg_authenc_hmac_md5_cbc_des, + &safexcel_alg_authenc_hmac_sha1_cbc_des, + &safexcel_alg_authenc_hmac_sha256_cbc_des, + &safexcel_alg_authenc_hmac_sha224_cbc_des, +--- a/drivers/crypto/inside-secure/safexcel.h ++++ b/drivers/crypto/inside-secure/safexcel.h +@@ -945,11 +945,13 @@ extern struct safexcel_alg_template safe + extern struct safexcel_alg_template safexcel_alg_hmac_sha256; + extern struct safexcel_alg_template safexcel_alg_hmac_sha384; + extern struct safexcel_alg_template safexcel_alg_hmac_sha512; ++extern struct safexcel_alg_template safexcel_alg_authenc_hmac_md5_cbc_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha1_cbc_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha224_cbc_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha256_cbc_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha384_cbc_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha512_cbc_aes; ++extern struct safexcel_alg_template safexcel_alg_authenc_hmac_md5_ctr_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha1_ctr_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha224_ctr_aes; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha256_ctr_aes; +@@ -982,11 +984,13 @@ extern struct safexcel_alg_template safe + extern struct safexcel_alg_template safexcel_alg_hmac_sha3_256; + extern struct safexcel_alg_template safexcel_alg_hmac_sha3_384; + extern struct safexcel_alg_template safexcel_alg_hmac_sha3_512; ++extern struct safexcel_alg_template safexcel_alg_authenc_hmac_md5_cbc_des3_ede; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha1_cbc_des3_ede; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha256_cbc_des3_ede; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha224_cbc_des3_ede; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha512_cbc_des3_ede; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha384_cbc_des3_ede; ++extern struct safexcel_alg_template safexcel_alg_authenc_hmac_md5_cbc_des; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha1_cbc_des; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha256_cbc_des; + extern struct safexcel_alg_template safexcel_alg_authenc_hmac_sha224_cbc_des; +--- a/drivers/crypto/inside-secure/safexcel_cipher.c ++++ b/drivers/crypto/inside-secure/safexcel_cipher.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -462,6 +463,9 @@ static int safexcel_aead_setkey(struct c + + /* Auth key */ + switch (ctx->hash_alg) { ++ case CONTEXT_CONTROL_CRYPTO_ALG_MD5: ++ alg = "safexcel-md5"; ++ break; + case CONTEXT_CONTROL_CRYPTO_ALG_SHA1: + alg = "safexcel-sha1"; + break; +@@ -1662,6 +1666,42 @@ static int safexcel_aead_cra_init(struct + return 0; + } + ++static int safexcel_aead_md5_cra_init(struct crypto_tfm *tfm) ++{ ++ struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ safexcel_aead_cra_init(tfm); ++ ctx->hash_alg = CONTEXT_CONTROL_CRYPTO_ALG_MD5; ++ ctx->state_sz = MD5_DIGEST_SIZE; ++ return 0; ++} ++ ++struct safexcel_alg_template safexcel_alg_authenc_hmac_md5_cbc_aes = { ++ .type = SAFEXCEL_ALG_TYPE_AEAD, ++ .algo_mask = SAFEXCEL_ALG_AES | SAFEXCEL_ALG_MD5, ++ .alg.aead = { ++ .setkey = safexcel_aead_setkey, ++ .encrypt = safexcel_aead_encrypt, ++ .decrypt = safexcel_aead_decrypt, ++ .ivsize = AES_BLOCK_SIZE, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),cbc(aes))", ++ .cra_driver_name = "safexcel-authenc-hmac-md5-cbc-aes", ++ .cra_priority = SAFEXCEL_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_ALLOCATES_MEMORY | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct safexcel_cipher_ctx), ++ .cra_alignmask = 0, ++ .cra_init = safexcel_aead_md5_cra_init, ++ .cra_exit = safexcel_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ + static int safexcel_aead_sha1_cra_init(struct crypto_tfm *tfm) + { + struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(tfm); +@@ -1842,6 +1882,43 @@ struct safexcel_alg_template safexcel_al + }, + }; + ++static int safexcel_aead_md5_des3_cra_init(struct crypto_tfm *tfm) ++{ ++ struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ safexcel_aead_md5_cra_init(tfm); ++ ctx->alg = SAFEXCEL_3DES; /* override default */ ++ ctx->blocksz = DES3_EDE_BLOCK_SIZE; ++ ctx->ivmask = EIP197_OPTION_2_TOKEN_IV_CMD; ++ return 0; ++} ++ ++struct safexcel_alg_template safexcel_alg_authenc_hmac_md5_cbc_des3_ede = { ++ .type = SAFEXCEL_ALG_TYPE_AEAD, ++ .algo_mask = SAFEXCEL_ALG_DES | SAFEXCEL_ALG_MD5, ++ .alg.aead = { ++ .setkey = safexcel_aead_setkey, ++ .encrypt = safexcel_aead_encrypt, ++ .decrypt = safexcel_aead_decrypt, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),cbc(des3_ede))", ++ .cra_driver_name = "safexcel-authenc-hmac-md5-cbc-des3_ede", ++ .cra_priority = SAFEXCEL_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_ALLOCATES_MEMORY | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct safexcel_cipher_ctx), ++ .cra_alignmask = 0, ++ .cra_init = safexcel_aead_md5_des3_cra_init, ++ .cra_exit = safexcel_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ + static int safexcel_aead_sha1_des3_cra_init(struct crypto_tfm *tfm) + { + struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(tfm); +@@ -2027,6 +2104,43 @@ struct safexcel_alg_template safexcel_al + }, + }; + ++static int safexcel_aead_md5_des_cra_init(struct crypto_tfm *tfm) ++{ ++ struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ safexcel_aead_md5_cra_init(tfm); ++ ctx->alg = SAFEXCEL_DES; /* override default */ ++ ctx->blocksz = DES_BLOCK_SIZE; ++ ctx->ivmask = EIP197_OPTION_2_TOKEN_IV_CMD; ++ return 0; ++} ++ ++struct safexcel_alg_template safexcel_alg_authenc_hmac_md5_cbc_des = { ++ .type = SAFEXCEL_ALG_TYPE_AEAD, ++ .algo_mask = SAFEXCEL_ALG_DES | SAFEXCEL_ALG_MD5, ++ .alg.aead = { ++ .setkey = safexcel_aead_setkey, ++ .encrypt = safexcel_aead_encrypt, ++ .decrypt = safexcel_aead_decrypt, ++ .ivsize = DES_BLOCK_SIZE, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),cbc(des))", ++ .cra_driver_name = "safexcel-authenc-hmac-md5-cbc-des", ++ .cra_priority = SAFEXCEL_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_ALLOCATES_MEMORY | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct safexcel_cipher_ctx), ++ .cra_alignmask = 0, ++ .cra_init = safexcel_aead_md5_des_cra_init, ++ .cra_exit = safexcel_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ + static int safexcel_aead_sha1_des_cra_init(struct crypto_tfm *tfm) + { + struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(tfm); +@@ -2209,6 +2323,41 @@ struct safexcel_alg_template safexcel_al + .cra_exit = safexcel_aead_cra_exit, + .cra_module = THIS_MODULE, + }, ++ }, ++}; ++ ++static int safexcel_aead_md5_ctr_cra_init(struct crypto_tfm *tfm) ++{ ++ struct safexcel_cipher_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ safexcel_aead_md5_cra_init(tfm); ++ ctx->mode = CONTEXT_CONTROL_CRYPTO_MODE_CTR_LOAD; /* override default */ ++ return 0; ++} ++ ++struct safexcel_alg_template safexcel_alg_authenc_hmac_md5_ctr_aes = { ++ .type = SAFEXCEL_ALG_TYPE_AEAD, ++ .algo_mask = SAFEXCEL_ALG_AES | SAFEXCEL_ALG_MD5, ++ .alg.aead = { ++ .setkey = safexcel_aead_setkey, ++ .encrypt = safexcel_aead_encrypt, ++ .decrypt = safexcel_aead_decrypt, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),rfc3686(ctr(aes)))", ++ .cra_driver_name = "safexcel-authenc-hmac-md5-ctr-aes", ++ .cra_priority = SAFEXCEL_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_ALLOCATES_MEMORY | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct safexcel_cipher_ctx), ++ .cra_alignmask = 0, ++ .cra_init = safexcel_aead_md5_ctr_cra_init, ++ .cra_exit = safexcel_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, + }, + }; + diff --git a/target/linux/generic/config-6.12 b/target/linux/generic/config-6.12 new file mode 100644 index 00000000000..37fdcc87f80 --- /dev/null +++ b/target/linux/generic/config-6.12 @@ -0,0 +1,7833 @@ +# CONFIG_104_QUAD_8 is not set +CONFIG_32BIT=y +# CONFIG_6LOWPAN is not set +# CONFIG_6LOWPAN_DEBUGFS is not set +# CONFIG_6PACK is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_9P_FS is not set +# CONFIG_AB8500_CORE is not set +# CONFIG_ABP060MG is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_ACENIC is not set +# CONFIG_ACERHDF is not set +# CONFIG_ACER_WIRELESS is not set +# CONFIG_ACER_WMI is not set +# CONFIG_ACORN_PARTITION is not set +# CONFIG_ACPI_AGDI is not set +# CONFIG_ACPI_ALS is not set +# CONFIG_ACPI_APEI is not set +# CONFIG_ACPI_APEI_PCIEAER is not set +# CONFIG_ACPI_BUTTON is not set +# CONFIG_ACPI_CONFIGFS is not set +# CONFIG_ACPI_EXTLOG is not set +# CONFIG_ACPI_HED is not set +# CONFIG_ACPI_NFIT is not set +# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set +# CONFIG_ACPI_TABLE_UPGRADE is not set +# CONFIG_ACPI_TOSHIBA is not set +# CONFIG_ACPI_VIDEO is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set +# CONFIG_AD2S90 is not set +# CONFIG_AD3552R is not set +# CONFIG_AD4000 is not set +# CONFIG_AD4130 is not set +# CONFIG_AD4695 is not set +# CONFIG_AD5064 is not set +# CONFIG_AD5110 is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_AD5272 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5592R is not set +# CONFIG_AD5593R is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5686 is not set +# CONFIG_AD5686_SPI is not set +# CONFIG_AD5696_I2C is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5758 is not set +# CONFIG_AD5761 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5766 is not set +# CONFIG_AD5770R is not set +# CONFIG_AD5791 is not set +# CONFIG_AD5933 is not set +# CONFIG_AD7091R5 is not set +# CONFIG_AD7091R8 is not set +# CONFIG_AD7124 is not set +# CONFIG_AD7150 is not set +# CONFIG_AD7173 is not set +# CONFIG_AD7192 is not set +# CONFIG_AD7266 is not set +# CONFIG_AD7280 is not set +# CONFIG_AD7291 is not set +# CONFIG_AD7292 is not set +# CONFIG_AD7293 is not set +# CONFIG_AD7298 is not set +# CONFIG_AD7303 is not set +# CONFIG_AD7380 is not set +# CONFIG_AD74115 is not set +# CONFIG_AD74413R is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7606 is not set +# CONFIG_AD7606_IFACE_PARALLEL is not set +# CONFIG_AD7606_IFACE_SPI is not set +# CONFIG_AD7746 is not set +# CONFIG_AD7766 is not set +# CONFIG_AD7768_1 is not set +# CONFIG_AD7780 is not set +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7887 is not set +# CONFIG_AD7923 is not set +# CONFIG_AD7944 is not set +# CONFIG_AD7949 is not set +# CONFIG_AD799X is not set +# CONFIG_AD8366 is not set +# CONFIG_AD8801 is not set +# CONFIG_AD9467 is not set +# CONFIG_AD9523 is not set +# CONFIG_AD9739A is not set +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set +# CONFIG_ADA4250 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_ADF4350 is not set +# CONFIG_ADF4371 is not set +# CONFIG_ADF4377 is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADIN1100_PHY is not set +# CONFIG_ADIN1110 is not set +# CONFIG_ADIN_PHY is not set +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16136 is not set +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_ADIS16260 is not set +# CONFIG_ADIS16400 is not set +# CONFIG_ADIS16460 is not set +# CONFIG_ADIS16475 is not set +# CONFIG_ADIS16480 is not set +# CONFIG_ADI_AXI_ADC is not set +# CONFIG_ADI_AXI_DAC is not set +# CONFIG_ADJD_S311 is not set +# CONFIG_ADM6996_PHY is not set +# CONFIG_ADM8211 is not set +# CONFIG_ADMFM2000 is not set +# CONFIG_ADMV1013 is not set +# CONFIG_ADMV1014 is not set +# CONFIG_ADMV4420 is not set +# CONFIG_ADMV8818 is not set +# CONFIG_ADRF6780 is not set +# CONFIG_ADT7316 is not set +# CONFIG_ADUX1020 is not set +CONFIG_ADVISE_SYSCALLS=y +# CONFIG_ADXL313_I2C is not set +# CONFIG_ADXL313_SPI is not set +# CONFIG_ADXL345_I2C is not set +# CONFIG_ADXL345_SPI is not set +# CONFIG_ADXL355_I2C is not set +# CONFIG_ADXL355_SPI is not set +# CONFIG_ADXL367_I2C is not set +# CONFIG_ADXL367_SPI is not set +# CONFIG_ADXL372_I2C is not set +# CONFIG_ADXL372_SPI is not set +# CONFIG_ADXL380_I2C is not set +# CONFIG_ADXL380_SPI is not set +# CONFIG_ADXRS290 is not set +# CONFIG_ADXRS450 is not set +CONFIG_AEABI=y +# CONFIG_AF8133J is not set +# CONFIG_AFE4403 is not set +# CONFIG_AFE4404 is not set +# CONFIG_AFFS_FS is not set +# CONFIG_AFS_DEBUG_CURSOR is not set +# CONFIG_AFS_FS is not set +# CONFIG_AF_KCM is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_AF_RXRPC_INJECT_LOSS is not set +# CONFIG_AF_RXRPC_INJECT_RX_DELAY is not set +# CONFIG_AF_RXRPC_IPV6 is not set +CONFIG_AF_UNIX_OOB=y +# CONFIG_AGP is not set +# CONFIG_AHCI_BRCM is not set +# CONFIG_AHCI_CEVA is not set +# CONFIG_AHCI_DWC is not set +# CONFIG_AHCI_IMX is not set +# CONFIG_AHCI_MVEBU is not set +# CONFIG_AHCI_QORIQ is not set +# CONFIG_AHCI_XGENE is not set +CONFIG_AIO=y +# CONFIG_AIR_EN8811H_PHY is not set +# CONFIG_AIX_PARTITION is not set +# CONFIG_AK09911 is not set +# CONFIG_AK8974 is not set +# CONFIG_AK8975 is not set +# CONFIG_AL3010 is not set +# CONFIG_AL3320A is not set +# CONFIG_ALIM7101_WDT is not set +CONFIG_ALLOW_DEV_COREDUMP=y +# CONFIG_ALTERA_MBOX is not set +# CONFIG_ALTERA_MSGDMA is not set +# CONFIG_ALTERA_STAPL is not set +# CONFIG_ALTERA_TSE is not set +# CONFIG_ALX is not set +# CONFIG_AL_FIC is not set +# CONFIG_AM2315 is not set +# CONFIG_AM335X_PHY_USB is not set +# CONFIG_AMBA_PL08X is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_AMD_MEM_ENCRYPT is not set +# CONFIG_AMD_PHY is not set +# CONFIG_AMD_QDMA is not set +# CONFIG_AMD_XGBE is not set +# CONFIG_AMD_XGBE_HAVE_ECC is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_AMILO_RFKILL is not set +# CONFIG_AMPERE_ERRATUM_AC03_CPU_38 is not set +# CONFIG_AMT is not set +# CONFIG_ANDROID_BINDER_IPC is not set +# CONFIG_ANON_VMA_NAME is not set +# CONFIG_AOSONG_AGS02MA is not set +# CONFIG_APDS9300 is not set +# CONFIG_APDS9306 is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_APDS9960 is not set +# CONFIG_APM_EMULATION is not set +# CONFIG_APPLE_GMUX is not set +# CONFIG_APPLE_MFI_FASTCHARGE is not set +# CONFIG_APPLE_PROPERTIES is not set +# CONFIG_APPLICOM is not set +# CONFIG_AQTION is not set +# CONFIG_AQUANTIA_PHY is not set +# CONFIG_AR5523 is not set +# CONFIG_AR8216_PHY is not set +# CONFIG_AR8216_PHY_LEDS is not set +# CONFIG_ARCH_ACTIONS is not set +# CONFIG_ARCH_AIROHA is not set +# CONFIG_ARCH_ALPINE is not set +# CONFIG_ARCH_APPLE is not set +# CONFIG_ARCH_ARTPEC is not set +# CONFIG_ARCH_ASPEED is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_AXXIA is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_ARCH_BCM2835 is not set +# CONFIG_ARCH_BCMBCA is not set +# CONFIG_ARCH_BCM_21664 is not set +# CONFIG_ARCH_BCM_23550 is not set +# CONFIG_ARCH_BCM_281XX is not set +# CONFIG_ARCH_BCM_5301X is not set +# CONFIG_ARCH_BCM_53573 is not set +# CONFIG_ARCH_BCM_CYGNUS is not set +# CONFIG_ARCH_BCM_HR2 is not set +# CONFIG_ARCH_BCM_IPROC is not set +# CONFIG_ARCH_BCM_NSP is not set +# CONFIG_ARCH_BERLIN is not set +CONFIG_ARCH_BINFMT_ELF_STATE=y +# CONFIG_ARCH_BITMAIN is not set +# CONFIG_ARCH_BRCMSTB is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_DAVINCI is not set +CONFIG_ARCH_DEFAULT_CRASH_DUMP=y +# CONFIG_ARCH_DIGICOLOR is not set +# CONFIG_ARCH_DMA_ADDR_T_64BIT is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_EXYNOS is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +# CONFIG_ARCH_FOOTBRIDGE is not set +CONFIG_ARCH_FORCE_MAX_ORDER=11 +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_HI3xxx is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_HISI is not set +# CONFIG_ARCH_HPE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_INTEL_SOCFPGA is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_K3 is not set +# CONFIG_ARCH_KEEMBAY is not set +# CONFIG_ARCH_KEYSTONE is not set +# CONFIG_ARCH_LAYERSCAPE is not set +# CONFIG_ARCH_LG1K is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MA35 is not set +# CONFIG_ARCH_MEDIATEK is not set +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_MILBEAUT is not set +CONFIG_ARCH_MMAP_RND_BITS=8 +CONFIG_ARCH_MMAP_RND_BITS_MAX=16 +CONFIG_ARCH_MMAP_RND_BITS_MIN=8 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8 +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_MSTARV7 is not set +# CONFIG_ARCH_MULTIPLATFORM is not set +# CONFIG_ARCH_MULTI_V6 is not set +# CONFIG_ARCH_MULTI_V7 is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_NPCM is not set +# CONFIG_ARCH_NSPIRE is not set +# CONFIG_ARCH_NXP is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_OMAP1 is not set +# CONFIG_ARCH_OMAP2 is not set +# CONFIG_ARCH_OMAP2PLUS is not set +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_PENSANDO is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_RDA is not set +# CONFIG_ARCH_REALTEK is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_RENESAS is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_S32 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SEATTLE is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_SPARX5 is not set +# CONFIG_ARCH_SPRD is not set +# CONFIG_ARCH_STI is not set +# CONFIG_ARCH_STM32 is not set +# CONFIG_ARCH_SUNPLUS is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_SYNQUACER is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_THUNDER is not set +# CONFIG_ARCH_THUNDER2 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_UNIPHIER is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_VIRT is not set +# CONFIG_ARCH_VISCONTI is not set +# CONFIG_ARCH_VT8500 is not set +# CONFIG_ARCH_WANTS_THP_SWAP is not set +# CONFIG_ARCH_WM8505 is not set +# CONFIG_ARCH_WM8750 is not set +# CONFIG_ARCH_WM8850 is not set +# CONFIG_ARCH_XGENE is not set +# CONFIG_ARCH_ZYNQ is not set +# CONFIG_ARCH_ZYNQMP is not set +# CONFIG_ARCNET is not set +# CONFIG_ARC_IRQ_NO_AUTOSAVE is not set +# CONFIG_ARM64_16K_PAGES is not set +# CONFIG_ARM64_64K_PAGES is not set +# CONFIG_ARM64_AMU_EXTN is not set +# CONFIG_ARM64_BTI is not set +CONFIG_ARM64_CNP=y +# CONFIG_ARM64_CONTPTE is not set +# CONFIG_ARM64_E0PD is not set +# CONFIG_ARM64_EPAN is not set +# CONFIG_ARM64_ERRATUM_1024718 is not set +# CONFIG_ARM64_ERRATUM_1165522 is not set +# CONFIG_ARM64_ERRATUM_1286807 is not set +# CONFIG_ARM64_ERRATUM_1319367 is not set +# CONFIG_ARM64_ERRATUM_1418040 is not set +# CONFIG_ARM64_ERRATUM_1463225 is not set +# CONFIG_ARM64_ERRATUM_1508412 is not set +# CONFIG_ARM64_ERRATUM_1530923 is not set +# CONFIG_ARM64_ERRATUM_1542419 is not set +# CONFIG_ARM64_ERRATUM_1742098 is not set +# CONFIG_ARM64_ERRATUM_2051678 is not set +# CONFIG_ARM64_ERRATUM_2054223 is not set +# CONFIG_ARM64_ERRATUM_2067961 is not set +# CONFIG_ARM64_ERRATUM_2077057 is not set +# CONFIG_ARM64_ERRATUM_2441007 is not set +# CONFIG_ARM64_ERRATUM_2441009 is not set +# CONFIG_ARM64_ERRATUM_2645198 is not set +# CONFIG_ARM64_ERRATUM_2658417 is not set +# CONFIG_ARM64_ERRATUM_2966298 is not set +# CONFIG_ARM64_ERRATUM_3117295 is not set +# CONFIG_ARM64_ERRATUM_3194386 is not set +# CONFIG_ARM64_ERRATUM_819472 is not set +# CONFIG_ARM64_ERRATUM_824069 is not set +# CONFIG_ARM64_ERRATUM_826319 is not set +# CONFIG_ARM64_ERRATUM_827319 is not set +# CONFIG_ARM64_ERRATUM_832075 is not set +# CONFIG_ARM64_ERRATUM_834220 is not set +# CONFIG_ARM64_ERRATUM_843419 is not set +# CONFIG_ARM64_ERRATUM_845719 is not set +# CONFIG_ARM64_ERRATUM_858921 is not set +# CONFIG_ARM64_HW_AFDBM is not set +# CONFIG_ARM64_LSE_ATOMICS is not set +# CONFIG_ARM64_MTE is not set +CONFIG_ARM64_PAN=y +# CONFIG_ARM64_PMEM is not set +# CONFIG_ARM64_POE is not set +# CONFIG_ARM64_PSEUDO_NMI is not set +# CONFIG_ARM64_PTR_AUTH is not set +# CONFIG_ARM64_RAS_EXTN is not set +# CONFIG_ARM64_RELOC_TEST is not set +# CONFIG_ARM64_SME is not set +# CONFIG_ARM64_SVE is not set +CONFIG_ARM64_SW_TTBR0_PAN=y +# CONFIG_ARM64_TLB_RANGE is not set +# CONFIG_ARM64_USE_LSE_ATOMICS is not set +# CONFIG_ARM64_VA_BITS_48 is not set +# CONFIG_ARM64_VA_BITS_52 is not set +# CONFIG_ARM_APPENDED_DTB is not set +# CONFIG_ARM_ARCH_TIMER is not set +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set +# CONFIG_ARM_CCI is not set +# CONFIG_ARM_CCI400_PMU is not set +# CONFIG_ARM_CCI5xx_PMU is not set +# CONFIG_ARM_CCI_PMU is not set +# CONFIG_ARM_CCN is not set +# CONFIG_ARM_CMN is not set +# CONFIG_ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU is not set +# CONFIG_ARM_CPUIDLE is not set +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_ARM_DEBUG_WX is not set +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_ARM_DSU_PMU is not set +# CONFIG_ARM_ERRATA_326103 is not set +# CONFIG_ARM_ERRATA_364296 is not set +# CONFIG_ARM_ERRATA_411920 is not set +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_643719 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764319 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_ARM_ERRATA_773022 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_ARM_ERRATA_798181 is not set +# CONFIG_ARM_ERRATA_814220 is not set +# CONFIG_ARM_ERRATA_818325_852422 is not set +# CONFIG_ARM_ERRATA_821420 is not set +# CONFIG_ARM_ERRATA_825619 is not set +# CONFIG_ARM_ERRATA_852421 is not set +# CONFIG_ARM_ERRATA_852423 is not set +# CONFIG_ARM_ERRATA_857271 is not set +# CONFIG_ARM_ERRATA_857272 is not set +# CONFIG_ARM_FFA_TRANSPORT is not set +CONFIG_ARM_GIC_MAX_NR=1 +# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set +# CONFIG_ARM_KPROBES_TEST is not set +# CONFIG_ARM_LPAE is not set +# CONFIG_ARM_MEDIATEK_CPUFREQ_HW is not set +# CONFIG_ARM_MHU is not set +CONFIG_ARM_MODULE_PLTS=y +# CONFIG_ARM_NI is not set +# CONFIG_ARM_PAN is not set +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +# CONFIG_ARM_PSCI is not set +# CONFIG_ARM_PSCI_CHECKER is not set +# CONFIG_ARM_PSCI_CPUIDLE is not set +# CONFIG_ARM_PTDUMP_DEBUGFS is not set +# CONFIG_ARM_SBSA_WATCHDOG is not set +# CONFIG_ARM_SCMI_PROTOCOL is not set +# CONFIG_ARM_SCPI_PROTOCOL is not set +# CONFIG_ARM_SDE_INTERFACE is not set +# CONFIG_ARM_SMCCC_SOC_ID is not set +# CONFIG_ARM_SMC_WATCHDOG is not set +# CONFIG_ARM_SMMU_V3_PMU is not set +# CONFIG_ARM_SP805_WATCHDOG is not set +# CONFIG_ARM_SPE_PMU is not set +# CONFIG_ARM_THUMBEE is not set +# CONFIG_ARM_TIMER_SP804 is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_ARM_VIRT_EXT is not set +# CONFIG_AS21XXX_PHY is not set +# CONFIG_AS3935 is not set +# CONFIG_AS73211 is not set +# CONFIG_ASM9260_TIMER is not set +# CONFIG_ASN1 is not set +# CONFIG_ASUS_LAPTOP is not set +# CONFIG_ASUS_TF103C_DOCK is not set +# CONFIG_ASUS_WIRELESS is not set +# CONFIG_ASUS_WMI is not set +# CONFIG_ASYMMETRIC_KEY_TYPE is not set +# CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE is not set +# CONFIG_ASYNC_RAID6_TEST is not set +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AT803X_PHY is not set +# CONFIG_AT91_SAMA5D2_ADC is not set +# CONFIG_ATA is not set +# CONFIG_ATAGS is not set +CONFIG_ATAGS_PROC=y +# CONFIG_ATALK is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_ATA_ACPI is not set +CONFIG_ATA_BMDMA=y +# CONFIG_ATA_FORCE is not set +# CONFIG_ATA_GENERIC is not set +# CONFIG_ATA_LEDS is not set +# CONFIG_ATA_NONSTANDARD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_ATA_PIIX is not set +CONFIG_ATA_SFF=y +# CONFIG_ATA_VERBOSE_ERROR is not set +# CONFIG_ATH10K is not set +# CONFIG_ATH25 is not set +# CONFIG_ATH5K is not set +# CONFIG_ATH6KL is not set +# CONFIG_ATH79 is not set +# CONFIG_ATH9K is not set +# CONFIG_ATH9K_HTC is not set +# CONFIG_ATH_DEBUG is not set +# CONFIG_ATL1 is not set +# CONFIG_ATL1C is not set +# CONFIG_ATL1E is not set +# CONFIG_ATL2 is not set +# CONFIG_ATLAS_EZO_SENSOR is not set +# CONFIG_ATLAS_PH_SENSOR is not set +# CONFIG_ATM is not set +# CONFIG_ATMEL_PIT is not set +# CONFIG_ATMEL_SSC is not set +# CONFIG_ATM_BR2684 is not set +CONFIG_ATM_BR2684_IPFILTER=y +# CONFIG_ATM_CLIP is not set +CONFIG_ATM_CLIP_NO_ICMP=y +# CONFIG_ATM_DRIVERS is not set +# CONFIG_ATM_DUMMY is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FORE200E is not set +# CONFIG_ATM_HE is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_LANE is not set +# CONFIG_ATM_MPOA is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_SOLOS is not set +# CONFIG_ATM_TCP is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_ATP is not set +# CONFIG_AUDIT is not set +# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTO_ZRELADDR is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_AW96103 is not set +# CONFIG_AX25 is not set +# CONFIG_AX25_DAMA_SLAVE is not set +# CONFIG_AX88796 is not set +# CONFIG_AX88796B_PHY is not set +# CONFIG_AXP20X_ADC is not set +# CONFIG_AXP20X_POWER is not set +# CONFIG_AXP288_ADC is not set +# CONFIG_AXP288_FUEL_GAUGE is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_B44 is not set +# CONFIG_B53 is not set +# CONFIG_B53_MDIO_DRIVER is not set +# CONFIG_B53_MMAP_DRIVER is not set +# CONFIG_B53_SERDES is not set +# CONFIG_B53_SPI_DRIVER is not set +# CONFIG_B53_SRAB_DRIVER is not set +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_APPLE is not set +# CONFIG_BACKLIGHT_ARCXCNN is not set +# CONFIG_BACKLIGHT_BD6107 is not set +# CONFIG_BACKLIGHT_CLASS_DEVICE is not set +# CONFIG_BACKLIGHT_GPIO is not set +# CONFIG_BACKLIGHT_KTD253 is not set +# CONFIG_BACKLIGHT_KTD2801 is not set +# CONFIG_BACKLIGHT_KTZ8866 is not set +# CONFIG_BACKLIGHT_LED is not set +# CONFIG_BACKLIGHT_LM3509 is not set +# CONFIG_BACKLIGHT_LM3630A is not set +# CONFIG_BACKLIGHT_LM3639 is not set +# CONFIG_BACKLIGHT_LP855X is not set +# CONFIG_BACKLIGHT_LV5207LP is not set +# CONFIG_BACKLIGHT_MP3309C is not set +# CONFIG_BACKLIGHT_PANDORA is not set +# CONFIG_BACKLIGHT_PWM is not set +# CONFIG_BACKLIGHT_QCOM_WLED is not set +# CONFIG_BACKLIGHT_SAHARA is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_BACKTRACE_VERBOSE is not set +# CONFIG_BAREUDP is not set +# CONFIG_BASE_SMALL is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_BATTERY_BQ27XXX is not set +# CONFIG_BATTERY_BQ27XXX_HDQ is not set +# CONFIG_BATTERY_CW2015 is not set +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_GAUGE_LTC2941 is not set +# CONFIG_BATTERY_GOLDFISH is not set +# CONFIG_BATTERY_LEGO_EV3 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_BATTERY_MAX1720X is not set +# CONFIG_BATTERY_MAX1721X is not set +# CONFIG_BATTERY_PM8916_BMS_VM is not set +# CONFIG_BATTERY_RT5033 is not set +# CONFIG_BATTERY_SAMSUNG_SDI is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BATTERY_UG3105 is not set +# CONFIG_BAYCOM_EPP is not set +# CONFIG_BAYCOM_PAR is not set +# CONFIG_BAYCOM_SER_FDX is not set +# CONFIG_BAYCOM_SER_HDX is not set +# CONFIG_BCACHE is not set +# CONFIG_BCACHEFS_FS is not set +# CONFIG_BCM47XX is not set +# CONFIG_BCM54140_PHY is not set +# CONFIG_BCM63XX is not set +# CONFIG_BCM63XX_PHY is not set +# CONFIG_BCM7038_L1_IRQ is not set +# CONFIG_BCM7038_WDT is not set +# CONFIG_BCM7120_L2_IRQ is not set +# CONFIG_BCM7XXX_PHY is not set +# CONFIG_BCM84881_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_BCMA is not set +# CONFIG_BCMA_DRIVER_GPIO is not set +CONFIG_BCMA_POSSIBLE=y +# CONFIG_BCMGENET is not set +# CONFIG_BCM_IPROC_ADC is not set +# CONFIG_BCM_KONA_USB2_PHY is not set +# CONFIG_BCM_SBA_RAID is not set +# CONFIG_BCM_VK is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_BE2ISCSI is not set +# CONFIG_BE2NET is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_BGMAC is not set +# CONFIG_BH1745 is not set +# CONFIG_BH1750 is not set +# CONFIG_BH1780 is not set +# CONFIG_BIG_KEYS is not set +# CONFIG_BIG_LITTLE is not set +CONFIG_BINARY_PRINTF=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_ELF_FDPIC is not set +# CONFIG_BINFMT_FLAT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_BINFMT_SCRIPT=y +CONFIG_BITREVERSE=y +# CONFIG_BLK_CGROUP_IOCOST is not set +# CONFIG_BLK_CGROUP_IOLATENCY is not set +# CONFIG_BLK_CGROUP_IOPRIO is not set +# CONFIG_BLK_DEBUG_FS is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_DM is not set +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_BLK_DEV_LOOP is not set +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +# CONFIG_BLK_DEV_MD is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_NULL_BLK is not set +# CONFIG_BLK_DEV_NVME is not set +# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set +# CONFIG_BLK_DEV_PMEM is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_RBD is not set +# CONFIG_BLK_DEV_SD is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_BLK_DEV_THROTTLING is not set +# CONFIG_BLK_DEV_UBLK is not set +CONFIG_BLK_DEV_WRITE_MOUNTED=y +# CONFIG_BLK_DEV_ZONED is not set +# CONFIG_BLK_INLINE_ENCRYPTION is not set +# CONFIG_BLK_SED_OPAL is not set +# CONFIG_BLK_WBT is not set +CONFIG_BLOCK=y +# CONFIG_BLOCK_LEGACY_AUTOLOAD is not set +# CONFIG_BLOCK_NOTIFIERS is not set +# CONFIG_BMA180 is not set +# CONFIG_BMA220 is not set +# CONFIG_BMA400 is not set +# CONFIG_BMC150_ACCEL is not set +# CONFIG_BMC150_MAGN is not set +# CONFIG_BMC150_MAGN_I2C is not set +# CONFIG_BMC150_MAGN_SPI is not set +# CONFIG_BME680 is not set +# CONFIG_BMG160 is not set +# CONFIG_BMI088_ACCEL is not set +# CONFIG_BMI160_I2C is not set +# CONFIG_BMI160_SPI is not set +# CONFIG_BMI323_I2C is not set +# CONFIG_BMI323_SPI is not set +# CONFIG_BMIPS_GENERIC is not set +# CONFIG_BMP280 is not set +# CONFIG_BNA is not set +# CONFIG_BNX2 is not set +# CONFIG_BNX2X is not set +# CONFIG_BNX2X_SRIOV is not set +# CONFIG_BNXT is not set +# CONFIG_BONDING is not set +# CONFIG_BOOKE_WDT is not set +CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT=3 +# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +# CONFIG_BOOTTIME_TRACING is not set +# CONFIG_BOOT_CONFIG is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +CONFIG_BOOT_RAW=y +# CONFIG_BOSCH_BNO055_I2C is not set +# CONFIG_BOSCH_BNO055_SERIAL is not set +# CONFIG_BOUNCE is not set +CONFIG_BPF=y +CONFIG_BPF_JIT=y +# CONFIG_BPF_JIT_ALWAYS_ON is not set +CONFIG_BPF_JIT_DEFAULT_ON=y +# CONFIG_BPF_LSM is not set +# CONFIG_BPF_PRELOAD is not set +# CONFIG_BPF_STREAM_PARSER is not set +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y +# CONFIG_BPQETHER is not set +CONFIG_BQL=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_BRCMFMAC is not set +# CONFIG_BRCMSMAC is not set +# CONFIG_BRCMSTB_GISB_ARB is not set +# CONFIG_BRCMSTB_L2_IRQ is not set +CONFIG_BRIDGE=y +# CONFIG_BRIDGE_CFM is not set +# CONFIG_BRIDGE_EBT_802_3 is not set +# CONFIG_BRIDGE_EBT_AMONG is not set +# CONFIG_BRIDGE_EBT_ARP is not set +# CONFIG_BRIDGE_EBT_ARPREPLY is not set +# CONFIG_BRIDGE_EBT_BROUTE is not set +# CONFIG_BRIDGE_EBT_DNAT is not set +# CONFIG_BRIDGE_EBT_IP is not set +# CONFIG_BRIDGE_EBT_IP6 is not set +# CONFIG_BRIDGE_EBT_LIMIT is not set +# CONFIG_BRIDGE_EBT_LOG is not set +# CONFIG_BRIDGE_EBT_MARK is not set +# CONFIG_BRIDGE_EBT_MARK_T is not set +# CONFIG_BRIDGE_EBT_NFLOG is not set +# CONFIG_BRIDGE_EBT_PKTTYPE is not set +# CONFIG_BRIDGE_EBT_REDIRECT is not set +# CONFIG_BRIDGE_EBT_SNAT is not set +# CONFIG_BRIDGE_EBT_STP is not set +# CONFIG_BRIDGE_EBT_T_FILTER is not set +# CONFIG_BRIDGE_EBT_T_NAT is not set +# CONFIG_BRIDGE_EBT_VLAN is not set +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_BRIDGE_MRP is not set +# CONFIG_BRIDGE_NETFILTER is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_BRIDGE_VLAN_FILTERING=y +# CONFIG_BROADCOM_PHY is not set +CONFIG_BROKEN_ON_SMP=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_BT is not set +# CONFIG_BTRFS_ASSERT is not set +# CONFIG_BTRFS_DEBUG is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_BTRFS_FS_POSIX_ACL is not set +# CONFIG_BTRFS_FS_REF_VERIFY is not set +# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set +# CONFIG_BT_AOSPEXT is not set +# CONFIG_BT_ATH3K is not set +# CONFIG_BT_BNEP is not set +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +# CONFIG_BT_BREDR is not set +# CONFIG_BT_CMTP is not set +# CONFIG_BT_FEATURE_DEBUG is not set +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBCM4377 is not set +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIBLUECARD is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBT3C is not set +# CONFIG_BT_HCIBTSDIO is not set +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTUSB_AUTOSUSPEND is not set +# CONFIG_BT_HCIBTUSB_MTK is not set +CONFIG_BT_HCIBTUSB_POLL_SYNC=y +# CONFIG_BT_HCIBTUSB_RTL is not set +# CONFIG_BT_HCIDTL1 is not set +# CONFIG_BT_HCIUART is not set +# CONFIG_BT_HCIUART_3WIRE is not set +# CONFIG_BT_HCIUART_AG6XX is not set +# CONFIG_BT_HCIUART_AML is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_LL is not set +# CONFIG_BT_HCIUART_MRVL is not set +# CONFIG_BT_HCIUART_QCA is not set +# CONFIG_BT_HCIUART_RTL is not set +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_HIDP is not set +# CONFIG_BT_INTEL_PCIE is not set +# CONFIG_BT_LE is not set +# CONFIG_BT_LEDS is not set +CONFIG_BT_LE_L2CAP_ECRED=y +# CONFIG_BT_MRVL is not set +# CONFIG_BT_MSFTEXT is not set +# CONFIG_BT_MTKSDIO is not set +# CONFIG_BT_MTKUART is not set +# CONFIG_BT_NXPUART is not set +# CONFIG_BT_RFCOMM is not set +CONFIG_BT_RFCOMM_TTY=y +# CONFIG_BT_SELFTEST is not set +# CONFIG_BT_VIRTIO is not set +CONFIG_BUG=y +# CONFIG_BUG_ON_DATA_CORRUPTION is not set +CONFIG_BUILDTIME_TABLE_SORT=y +CONFIG_BUILD_SALT="" +# CONFIG_C2PORT is not set +# CONFIG_CACHESTAT_SYSCALL is not set +CONFIG_CACHE_L2X0_PMU=y +# CONFIG_CADENCE_WATCHDOG is not set +# CONFIG_CAIF is not set +# CONFIG_CAN is not set +# CONFIG_CAN_BCM is not set +# CONFIG_CAN_CAN327 is not set +# CONFIG_CAN_CTUCANFD_PCI is not set +# CONFIG_CAN_CTUCANFD_PLATFORM is not set +# CONFIG_CAN_DEBUG_DEVICES is not set +# CONFIG_CAN_DEV is not set +# CONFIG_CAN_ESD_402_PCI is not set +# CONFIG_CAN_ESD_USB is not set +# CONFIG_CAN_ETAS_ES58X is not set +# CONFIG_CAN_F81604 is not set +# CONFIG_CAN_GS_USB is not set +# CONFIG_CAN_GW is not set +# CONFIG_CAN_HI311X is not set +# CONFIG_CAN_IFI_CANFD is not set +# CONFIG_CAN_ISOTP is not set +# CONFIG_CAN_J1939 is not set +# CONFIG_CAN_KVASER_PCIEFD is not set +# CONFIG_CAN_MCBA_USB is not set +# CONFIG_CAN_MCP251XFD is not set +# CONFIG_CAN_M_CAN is not set +# CONFIG_CAN_NETLINK is not set +# CONFIG_CAN_PEAK_PCIEFD is not set +# CONFIG_CAN_RAW is not set +# CONFIG_CAN_RCAR is not set +# CONFIG_CAN_RCAR_CANFD is not set +# CONFIG_CAN_ROCKCHIP_CANFD is not set +# CONFIG_CAN_SLCAN is not set +# CONFIG_CAN_SUN4I is not set +# CONFIG_CAN_UCAN is not set +# CONFIG_CAN_VCAN is not set +# CONFIG_CAN_VXCAN is not set +# CONFIG_CAPI_TRACE is not set +CONFIG_CARDBUS=y +# CONFIG_CARL9170 is not set +# CONFIG_CASSINI is not set +# CONFIG_CAVIUM_CPT is not set +# CONFIG_CAVIUM_ERRATUM_22375 is not set +# CONFIG_CAVIUM_ERRATUM_23144 is not set +# CONFIG_CAVIUM_ERRATUM_23154 is not set +# CONFIG_CAVIUM_ERRATUM_27456 is not set +# CONFIG_CAVIUM_ERRATUM_30115 is not set +# CONFIG_CAVIUM_OCTEON_SOC is not set +# CONFIG_CAVIUM_PTP is not set +# CONFIG_CAVIUM_TX2_ERRATUM_219 is not set +# CONFIG_CB710_CORE is not set +# CONFIG_CC10001_ADC is not set +# CONFIG_CCS811 is not set +CONFIG_CC_CAN_LINK=y +CONFIG_CC_NO_STRINGOP_OVERFLOW=y +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_CDX_BUS is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_CFG80211 is not set +# CONFIG_CFG80211_CERTIFICATION_ONUS is not set +CONFIG_CFG80211_HEADERS=y +# CONFIG_CGROUPS is not set +# CONFIG_CGROUP_FAVOR_DYNMODS is not set +# CONFIG_CGROUP_MISC is not set +# CONFIG_CHARGER_ADP5061 is not set +# CONFIG_CHARGER_BD99954 is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_BQ24190 is not set +# CONFIG_CHARGER_BQ24257 is not set +# CONFIG_CHARGER_BQ24735 is not set +# CONFIG_CHARGER_BQ2515X is not set +# CONFIG_CHARGER_BQ256XX is not set +# CONFIG_CHARGER_BQ25890 is not set +# CONFIG_CHARGER_BQ25980 is not set +# CONFIG_CHARGER_DETECTOR_MAX14656 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_LT3651 is not set +# CONFIG_CHARGER_LTC4162L is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_MAX77976 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_PM8916_LBC is not set +# CONFIG_CHARGER_QCOM_SMB2 is not set +# CONFIG_CHARGER_QCOM_SMBB is not set +# CONFIG_CHARGER_RT9455 is not set +# CONFIG_CHARGER_RT9467 is not set +# CONFIG_CHARGER_RT9471 is not set +# CONFIG_CHARGER_SBS is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_CHARGER_TWL4030 is not set +# CONFIG_CHARGER_UCS1002 is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_CHELSIO_T1 is not set +# CONFIG_CHELSIO_T3 is not set +# CONFIG_CHELSIO_T4 is not set +# CONFIG_CHELSIO_T4VF is not set +# CONFIG_CHROME_PLATFORMS is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_CIFS is not set +CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y +# CONFIG_CIFS_COMPRESSION is not set +# CONFIG_CIFS_DEBUG is not set +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_FSCACHE is not set +# CONFIG_CIFS_NFSD_EXPORT is not set +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_STATS2 is not set +# CONFIG_CIFS_SWN_UPCALL is not set +CONFIG_CIFS_XATTR=y +# CONFIG_CIO_DAC is not set +# CONFIG_CLKSRC_PISTACHIO is not set +# CONFIG_CLKSRC_VERSATILE is not set +# CONFIG_CLK_GFM_LPASS_SM8250 is not set +# CONFIG_CLK_HSDK is not set +# CONFIG_CLK_ICST is not set +# CONFIG_CLK_QORIQ is not set +# CONFIG_CLK_SP810 is not set +# CONFIG_CLK_TWL is not set +CONFIG_CLS_U32_MARK=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CM32181 is not set +# CONFIG_CM3232 is not set +# CONFIG_CM3323 is not set +# CONFIG_CM3605 is not set +# CONFIG_CM36651 is not set +# CONFIG_CMA is not set +CONFIG_CMDLINE="" +# CONFIG_CMDLINE_BOOL is not set +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_CMDLINE_FROM_BOOTLOADER is not set +# CONFIG_CMDLINE_PARTITION is not set +# CONFIG_CNIC is not set +# CONFIG_CODA_FS is not set +# CONFIG_CODE_PATCHING_SELFTEST is not set +# CONFIG_COMEDI is not set +# CONFIG_COMMON_CLK_AXI_CLKGEN is not set +# CONFIG_COMMON_CLK_BOSTON is not set +# CONFIG_COMMON_CLK_CDCE706 is not set +# CONFIG_COMMON_CLK_CDCE925 is not set +# CONFIG_COMMON_CLK_CS2000_CP is not set +# CONFIG_COMMON_CLK_FIXED_MMIO is not set +# CONFIG_COMMON_CLK_IPROC is not set +# CONFIG_COMMON_CLK_MAX9485 is not set +# CONFIG_COMMON_CLK_MEDIATEK_FHCTL is not set +# CONFIG_COMMON_CLK_MT6765 is not set +# CONFIG_COMMON_CLK_MT8167 is not set +# CONFIG_COMMON_CLK_MT8167_AUDSYS is not set +# CONFIG_COMMON_CLK_MT8167_IMGSYS is not set +# CONFIG_COMMON_CLK_MT8167_MFGCFG is not set +# CONFIG_COMMON_CLK_MT8167_MMSYS is not set +# CONFIG_COMMON_CLK_MT8167_VDECSYS is not set +# CONFIG_COMMON_CLK_MT8188 is not set +# CONFIG_COMMON_CLK_MT8192 is not set +# CONFIG_COMMON_CLK_NXP is not set +# CONFIG_COMMON_CLK_PIC32 is not set +# CONFIG_COMMON_CLK_PISTACHIO is not set +# CONFIG_COMMON_CLK_PWM is not set +# CONFIG_COMMON_CLK_PXA is not set +# CONFIG_COMMON_CLK_QCOM is not set +# CONFIG_COMMON_CLK_RS9_PCIE is not set +# CONFIG_COMMON_CLK_SI514 is not set +# CONFIG_COMMON_CLK_SI521XX is not set +# CONFIG_COMMON_CLK_SI5341 is not set +# CONFIG_COMMON_CLK_SI5351 is not set +# CONFIG_COMMON_CLK_SI544 is not set +# CONFIG_COMMON_CLK_SI570 is not set +# CONFIG_COMMON_CLK_VC3 is not set +# CONFIG_COMMON_CLK_VC5 is not set +# CONFIG_COMMON_CLK_VC7 is not set +# CONFIG_COMMON_CLK_XGENE is not set +# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set +CONFIG_COMPACTION=y +# CONFIG_COMPAL_LAPTOP is not set +# CONFIG_COMPAT is not set +# CONFIG_COMPAT_BRK is not set +# CONFIG_COMPILE_TEST is not set +# CONFIG_COMPRESSED_INSTALL is not set +# CONFIG_CONFIGFS_FS is not set +# CONFIG_CONNECTOR is not set +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 +CONFIG_CONSOLE_LOGLEVEL_QUIET=4 +CONFIG_CONSTRUCTORS=y +# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_CORDIC is not set +# CONFIG_COREDUMP is not set +# CONFIG_CORESIGHT is not set +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_CORTINA_PHY is not set +# CONFIG_COUNTER is not set +# CONFIG_CPA_DEBUG is not set +# CONFIG_CPUFREQ_DT is not set +# CONFIG_CPUFREQ_DT_PLATDEV is not set +# CONFIG_CPU_BIG_ENDIAN is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_GOV_SCHEDUTIL is not set +# CONFIG_CPU_FREQ_THERMAL is not set +# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_ICACHE_MISMATCH_WORKAROUND is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_CPU_IDLE_GOV_LADDER is not set +# CONFIG_CPU_IDLE_GOV_MENU is not set +# CONFIG_CPU_IDLE_GOV_TEO is not set +# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set +# CONFIG_CPU_ISOLATION is not set +# CONFIG_CPU_LITTLE_ENDIAN is not set +# CONFIG_CPU_NO_EFFICIENT_FFS is not set +CONFIG_CPU_SW_DOMAIN_PAN=y +# CONFIG_CPU_THERMAL is not set +# CONFIG_CRAMFS is not set +CONFIG_CRAMFS_BLOCKDEV=y +# CONFIG_CRAMFS_MTD is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_CRASH_HOTPLUG is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_SELFTEST is not set +# CONFIG_CRC32_SLICEBY4 is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC4 is not set +# CONFIG_CRC64 is not set +# CONFIG_CRC64_ROCKSOFT is not set +# CONFIG_CRC7 is not set +# CONFIG_CRC8 is not set +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC_ITU_T is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CROSS_MEMORY_ATTACH is not set +# CONFIG_CROS_HPS_I2C is not set +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_842 is not set +CONFIG_CRYPTO_ACOMP2=y +# CONFIG_CRYPTO_ADIANTUM is not set +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +# CONFIG_CRYPTO_AEGIS128 is not set +# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_ARM is not set +# CONFIG_CRYPTO_AES_ARM64 is not set +# CONFIG_CRYPTO_AES_ARM64_BS is not set +# CONFIG_CRYPTO_AES_ARM64_CE is not set +# CONFIG_CRYPTO_AES_ARM64_CE_BLK is not set +# CONFIG_CRYPTO_AES_ARM64_CE_CCM is not set +# CONFIG_CRYPTO_AES_ARM64_NEON_BLK is not set +# CONFIG_CRYPTO_AES_ARM_BS is not set +# CONFIG_CRYPTO_AES_ARM_CE is not set +# CONFIG_CRYPTO_AES_NI_INTEL is not set +# CONFIG_CRYPTO_AES_RISCV64 is not set +# CONFIG_CRYPTO_AES_TI is not set +CONFIG_CRYPTO_AKCIPHER=y +CONFIG_CRYPTO_AKCIPHER2=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_ARIA is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_BLAKE2B is not set +# CONFIG_CRYPTO_BLAKE2B_NEON is not set +# CONFIG_CRYPTO_BLAKE2S_ARM is not set +# CONFIG_CRYPTO_BLAKE2S_X86 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_CBC is not set +CONFIG_CRYPTO_CCM=y +# CONFIG_CRYPTO_CHACHA20 is not set +# CONFIG_CRYPTO_CHACHA20POLY1305 is not set +# CONFIG_CRYPTO_CHACHA20_NEON is not set +# CONFIG_CRYPTO_CHACHA20_X86_64 is not set +# CONFIG_CRYPTO_CHACHA_MIPS is not set +# CONFIG_CRYPTO_CHACHA_RISCV64 is not set +# CONFIG_CRYPTO_CMAC is not set +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CRC32C_INTEL is not set +# CONFIG_CRYPTO_CRC32_ARM_CE is not set +# CONFIG_CRYPTO_CRCT10DIF is not set +# CONFIG_CRYPTO_CRCT10DIF_ARM64_CE is not set +# CONFIG_CRYPTO_CRCT10DIF_ARM_CE is not set +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_CTR=y +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_CURVE25519 is not set +# CONFIG_CRYPTO_CURVE25519_NEON is not set +# CONFIG_CRYPTO_CURVE25519_X86 is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set +# CONFIG_CRYPTO_DEV_ATMEL_AES is not set +# CONFIG_CRYPTO_DEV_ATMEL_AUTHENC is not set +# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set +# CONFIG_CRYPTO_DEV_ATMEL_SHA is not set +# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set +# CONFIG_CRYPTO_DEV_ATMEL_TDES is not set +# CONFIG_CRYPTO_DEV_CAVIUM_ZIP is not set +# CONFIG_CRYPTO_DEV_CCP is not set +# CONFIG_CRYPTO_DEV_CCP_DEBUGFS is not set +# CONFIG_CRYPTO_DEV_CCREE is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM_DEBUG is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM_INTC is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST is not set +# CONFIG_CRYPTO_DEV_HIFN_795X is not set +# CONFIG_CRYPTO_DEV_HISI_SEC is not set +# CONFIG_CRYPTO_DEV_HISI_ZIP is not set +# CONFIG_CRYPTO_DEV_IMGTEC_HASH is not set +# CONFIG_CRYPTO_DEV_MARVELL_CESA is not set +# CONFIG_CRYPTO_DEV_MXS_DCP is not set +# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set +# CONFIG_CRYPTO_DEV_OCTEONTX_CPT is not set +# CONFIG_CRYPTO_DEV_QAT_420XX is not set +# CONFIG_CRYPTO_DEV_QAT_4XXX is not set +# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set +# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set +# CONFIG_CRYPTO_DEV_QAT_C62X is not set +# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set +# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set +# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set +# CONFIG_CRYPTO_DEV_QCE is not set +# CONFIG_CRYPTO_DEV_S5P is not set +# CONFIG_CRYPTO_DEV_SAFEXCEL is not set +# CONFIG_CRYPTO_DEV_SAHARA is not set +# CONFIG_CRYPTO_DEV_SP_PSP is not set +# CONFIG_CRYPTO_DEV_TALITOS is not set +# CONFIG_CRYPTO_DEV_VIRTIO is not set +# CONFIG_CRYPTO_DH is not set +# CONFIG_CRYPTO_DRBG_CTR is not set +# CONFIG_CRYPTO_DRBG_HASH is not set +# CONFIG_CRYPTO_DRBG_MENU is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_ECDH is not set +# CONFIG_CRYPTO_ECDSA is not set +# CONFIG_CRYPTO_ECHAINIV is not set +# CONFIG_CRYPTO_ECRDSA is not set +# CONFIG_CRYPTO_ESSIV is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_FIPS is not set +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_GHASH=y +# CONFIG_CRYPTO_GHASH_ARM64_CE is not set +# CONFIG_CRYPTO_GHASH_ARM_CE is not set +# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set +# CONFIG_CRYPTO_GHASH_RISCV64 is not set +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +# CONFIG_CRYPTO_HCTR2 is not set +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_CRYPTO_JITTERENTROPY is not set +# CONFIG_CRYPTO_JITTERENTROPY_TESTINTERFACE is not set +# CONFIG_CRYPTO_KEYWRAP is not set +# CONFIG_CRYPTO_KHAZAD is not set +CONFIG_CRYPTO_KPP=y +CONFIG_CRYPTO_KPP2=y +CONFIG_CRYPTO_LIB_AES=y +CONFIG_CRYPTO_LIB_ARC4=y +# CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC is not set +# CONFIG_CRYPTO_LIB_CHACHA is not set +# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set +# CONFIG_CRYPTO_LIB_CURVE25519 is not set +# CONFIG_CRYPTO_LIB_POLY1305 is not set +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9 +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set +# CONFIG_CRYPTO_LZO is not set +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_NHPOLY1305_NEON is not set +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_NULL2=y +# CONFIG_CRYPTO_PCBC is not set +CONFIG_CRYPTO_PCRYPT=y +# CONFIG_CRYPTO_POLY1305 is not set +# CONFIG_CRYPTO_POLY1305_ARM is not set +# CONFIG_CRYPTO_POLY1305_MIPS is not set +# CONFIG_CRYPTO_POLY1305_NEON is not set +# CONFIG_CRYPTO_POLY1305_X86_64 is not set +# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RNG is not set +# CONFIG_CRYPTO_RSA is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SEQIV is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA1_ARM is not set +# CONFIG_CRYPTO_SHA1_ARM64_CE is not set +# CONFIG_CRYPTO_SHA1_ARM_CE is not set +# CONFIG_CRYPTO_SHA1_ARM_NEON is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA256_ARM is not set +# CONFIG_CRYPTO_SHA256_ARM64 is not set +# CONFIG_CRYPTO_SHA256_RISCV64 is not set +# CONFIG_CRYPTO_SHA2_ARM64_CE is not set +# CONFIG_CRYPTO_SHA2_ARM_CE is not set +# CONFIG_CRYPTO_SHA3 is not set +# CONFIG_CRYPTO_SHA3_ARM64 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_SHA512_ARM is not set +# CONFIG_CRYPTO_SHA512_ARM64 is not set +# CONFIG_CRYPTO_SHA512_ARM64_CE is not set +# CONFIG_CRYPTO_SHA512_RISCV64 is not set +# CONFIG_CRYPTO_SIMD is not set +CONFIG_CRYPTO_SKCIPHER=y +CONFIG_CRYPTO_SKCIPHER2=y +# CONFIG_CRYPTO_SM3 is not set +# CONFIG_CRYPTO_SM3_ARM64_CE is not set +# CONFIG_CRYPTO_SM3_GENERIC is not set +# CONFIG_CRYPTO_SM3_NEON is not set +# CONFIG_CRYPTO_SM3_RISCV64 is not set +# CONFIG_CRYPTO_SM4 is not set +# CONFIG_CRYPTO_SM4_ARM64_CE is not set +# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set +# CONFIG_CRYPTO_SM4_ARM64_CE_CCM is not set +# CONFIG_CRYPTO_SM4_ARM64_CE_GCM is not set +# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set +# CONFIG_CRYPTO_SM4_GENERIC is not set +# CONFIG_CRYPTO_SM4_RISCV64 is not set +# CONFIG_CRYPTO_STREEBOG is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TEST is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_TWOFISH_586 is not set +# CONFIG_CRYPTO_TWOFISH_COMMON is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_USER_API_AEAD is not set +# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_RNG is not set +# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_VMAC is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_XTS is not set +# CONFIG_CRYPTO_XXHASH is not set +# CONFIG_CRYPTO_ZSTD is not set +# CONFIG_CS5535_MFGPT is not set +# CONFIG_CS89x0 is not set +# CONFIG_CS89x0_PLATFORM is not set +# CONFIG_CSD_LOCK_WAIT_DEBUG is not set +# CONFIG_CUSE is not set +# CONFIG_CW1200 is not set +# CONFIG_CXD2880_SPI_DRV is not set +# CONFIG_CXL_BASE is not set +# CONFIG_CXL_BUS is not set +# CONFIG_CYPRESS_FIRMWARE is not set +# CONFIG_CZNIC_PLATFORMS is not set +# CONFIG_DA280 is not set +# CONFIG_DA311 is not set +# CONFIG_DAMON is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_DAX is not set +# CONFIG_DCB is not set +# CONFIG_DDR is not set +# CONFIG_DEBUG_ALIGN_RODATA is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_CGROUP_REF is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_EFI is not set +# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_FS_ALLOW_ALL=y +# CONFIG_DEBUG_FS_ALLOW_NONE is not set +# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set +# CONFIG_DEBUG_GPIO is not set +# CONFIG_DEBUG_HIGHMEM is not set +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_INFO_BTF is not set +CONFIG_DEBUG_INFO_COMPRESSED_NONE=y +# CONFIG_DEBUG_INFO_COMPRESSED_ZLIB is not set +# CONFIG_DEBUG_INFO_COMPRESSED_ZSTD is not set +# CONFIG_DEBUG_INFO_DWARF4 is not set +# CONFIG_DEBUG_INFO_DWARF5 is not set +CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y +# CONFIG_DEBUG_INFO_NONE is not set +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_INFO_SPLIT is not set +# CONFIG_DEBUG_IRQFLAGS is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_KMAP_LOCAL is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_KOBJECT_RELEASE is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_DEBUG_LL_UART_8250 is not set +# CONFIG_DEBUG_LL_UART_PL01X is not set +# CONFIG_DEBUG_LOCKDEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_DEBUG_MAPLE_TREE is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_MISC is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_NET is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_PAGE_REF is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_DEBUG_PINCTRL is not set +# CONFIG_DEBUG_PLIST is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RODATA_TEST is not set +# CONFIG_DEBUG_RSEQ is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_DEBUG_RWSEMS is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_DEBUG_SEMIHOSTING is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set +# CONFIG_DEBUG_UART_8250_PALMCHIP is not set +# CONFIG_DEBUG_UART_8250_WORD is not set +# CONFIG_DEBUG_UART_FLOW_CONTROL is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_VIRTUAL is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_VM_MAPLE_TREE is not set +# CONFIG_DEBUG_VM_PGFLAGS is not set +# CONFIG_DEBUG_VM_PGTABLE is not set +# CONFIG_DEBUG_VM_RB is not set +# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +# CONFIG_DEBUG_WX is not set +# CONFIG_DEBUG_ZBOOT is not set +# CONFIG_DEFAULT_CODEL is not set +CONFIG_DEFAULT_CUBIC=y +# CONFIG_DEFAULT_FQ is not set +CONFIG_DEFAULT_FQ_CODEL=y +# CONFIG_DEFAULT_FQ_PIE is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +CONFIG_DEFAULT_INIT="" +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_DEFAULT_NET_SCH="fq_codel" +# CONFIG_DEFAULT_PFIFO_FAST is not set +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SFQ is not set +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set +# CONFIG_DELL_LAPTOP is not set +# CONFIG_DELL_RBTN is not set +# CONFIG_DELL_SMBIOS is not set +# CONFIG_DELL_SMO8800 is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_DEVMEM is not set +CONFIG_DEVPORT=y +# CONFIG_DEVTMPFS is not set +# CONFIG_DEVTMPFS_MOUNT is not set +# CONFIG_DEVTMPFS_SAFE is not set +# CONFIG_DEV_DAX is not set +# CONFIG_DHT11 is not set +CONFIG_DIMLIB=y +# CONFIG_DL2K is not set +# CONFIG_DLHL60D is not set +# CONFIG_DLM is not set +# CONFIG_DM9000 is not set +# CONFIG_DM9051 is not set +# CONFIG_DMABUF_DEBUG is not set +# CONFIG_DMABUF_HEAPS is not set +# CONFIG_DMABUF_MOVE_NOTIFY is not set +# CONFIG_DMABUF_SELFTESTS is not set +# CONFIG_DMABUF_SYSFS_STATS is not set +# CONFIG_DMADEVICES is not set +# CONFIG_DMADEVICES_DEBUG is not set +# CONFIG_DMAPOOL_TEST is not set +# CONFIG_DMARD06 is not set +# CONFIG_DMARD09 is not set +# CONFIG_DMARD10 is not set +# CONFIG_DMATEST is not set +# CONFIG_DMA_API_DEBUG is not set +CONFIG_DMA_COHERENT_POOL=y +CONFIG_DMA_DECLARE_COHERENT=y +# CONFIG_DMA_ENGINE is not set +# CONFIG_DMA_FENCE_TRACE is not set +# CONFIG_DMA_JZ4780 is not set +# CONFIG_DMA_MAP_BENCHMARK is not set +CONFIG_DMA_NONCOHERENT_MMAP=y +# CONFIG_DMA_RESTRICTED_POOL is not set +# CONFIG_DMA_SHARED_BUFFER is not set +# CONFIG_DM_AUDIT is not set +# CONFIG_DM_CACHE is not set +# CONFIG_DM_CLONE is not set +# CONFIG_DM_DEBUG is not set +# CONFIG_DM_DELAY is not set +# CONFIG_DM_DUST is not set +# CONFIG_DM_EBS is not set +# CONFIG_DM_ERA is not set +# CONFIG_DM_FLAKEY is not set +# CONFIG_DM_INTEGRITY is not set +# CONFIG_DM_LOG_USERSPACE is not set +# CONFIG_DM_LOG_WRITES is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_SWITCH is not set +# CONFIG_DM_THIN_PROVISIONING is not set +# CONFIG_DM_UEVENT is not set +# CONFIG_DM_UNSTRIPED is not set +# CONFIG_DM_VDO is not set +# CONFIG_DM_VERITY is not set +# CONFIG_DM_WRITECACHE is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_ZONED is not set +# CONFIG_DNET is not set +# CONFIG_DNOTIFY is not set +# CONFIG_DNS_RESOLVER is not set +# CONFIG_DP83640_PHY is not set +# CONFIG_DP83822_PHY is not set +# CONFIG_DP83848_PHY is not set +# CONFIG_DP83867_PHY is not set +# CONFIG_DP83869_PHY is not set +# CONFIG_DP83TC811_PHY is not set +# CONFIG_DP83TD510_PHY is not set +# CONFIG_DP83TG720_PHY is not set +# CONFIG_DPM_WATCHDOG is not set +# CONFIG_DPOT_DAC is not set +# CONFIG_DPS310 is not set +CONFIG_DQL=y +# CONFIG_DRAGONRISE_FF is not set +# CONFIG_DRM is not set +# CONFIG_DRM_ACCEL is not set +# CONFIG_DRM_ACCEL_HABANALABS is not set +# CONFIG_DRM_ACCEL_IVPU is not set +# CONFIG_DRM_ACCEL_QAIC is not set +# CONFIG_DRM_AMDGPU is not set +# CONFIG_DRM_AMDGPU_CIK is not set +# CONFIG_DRM_AMDGPU_SI is not set +# CONFIG_DRM_AMDGPU_USERPTR is not set +# CONFIG_DRM_AMDGPU_WERROR is not set +# CONFIG_DRM_AMD_ACP is not set +# CONFIG_DRM_AMD_DC_SI is not set +# CONFIG_DRM_AMD_ISP is not set +# CONFIG_DRM_AMD_SECURE_DISPLAY is not set +# CONFIG_DRM_ANALOGIX_ANX6345 is not set +# CONFIG_DRM_ANALOGIX_ANX7625 is not set +# CONFIG_DRM_ANALOGIX_ANX78XX is not set +# CONFIG_DRM_ARCPGU is not set +# CONFIG_DRM_ARMADA is not set +# CONFIG_DRM_AST is not set +# CONFIG_DRM_ATMEL_HLCDC is not set +# CONFIG_DRM_BOCHS is not set +# CONFIG_DRM_CDNS_DSI is not set +# CONFIG_DRM_CDNS_MHDP8546 is not set +# CONFIG_DRM_CHIPONE_ICN6211 is not set +# CONFIG_DRM_CHRONTEL_CH7033 is not set +# CONFIG_DRM_CIRRUS_QEMU is not set +# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set +# CONFIG_DRM_DEBUG_MM is not set +# CONFIG_DRM_DEBUG_MODESET_LOCK is not set +# CONFIG_DRM_DISPLAY_CONNECTOR is not set +# CONFIG_DRM_DISPLAY_DP_AUX_CEC is not set +# CONFIG_DRM_DISPLAY_DP_AUX_CHARDEV is not set +# CONFIG_DRM_DW_HDMI_CEC is not set +# CONFIG_DRM_ETNAVIV is not set +# CONFIG_DRM_EXYNOS is not set +# CONFIG_DRM_FBDEV_EMULATION is not set +# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set +# CONFIG_DRM_FSL_DCU is not set +# CONFIG_DRM_GM12U320 is not set +# CONFIG_DRM_GMA500 is not set +# CONFIG_DRM_GUD is not set +# CONFIG_DRM_HDLCD is not set +# CONFIG_DRM_HISI_HIBMC is not set +# CONFIG_DRM_HISI_KIRIN is not set +# CONFIG_DRM_I2C_ADV7511 is not set +# CONFIG_DRM_I2C_CH7006 is not set +# CONFIG_DRM_I2C_NXP_TDA9950 is not set +# CONFIG_DRM_I2C_NXP_TDA998X is not set +# CONFIG_DRM_I2C_SIL164 is not set +# CONFIG_DRM_I915 is not set +# CONFIG_DRM_I915_DEBUG_WAKEREF is not set +# CONFIG_DRM_I915_GVT_KVMGT is not set +# CONFIG_DRM_I915_REPLAY_GPU_HANGS_API is not set +# CONFIG_DRM_IMX_LCDIF is not set +# CONFIG_DRM_ITE_IT6505 is not set +# CONFIG_DRM_ITE_IT66121 is not set +# CONFIG_DRM_KOMEDA is not set +# CONFIG_DRM_LIB_RANDOM is not set +# CONFIG_DRM_LIMA is not set +# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set +# CONFIG_DRM_LOGICVC is not set +# CONFIG_DRM_LONTIUM_LT8912B is not set +# CONFIG_DRM_LONTIUM_LT9211 is not set +# CONFIG_DRM_LONTIUM_LT9611 is not set +# CONFIG_DRM_LONTIUM_LT9611UXC is not set +# CONFIG_DRM_LOONGSON is not set +# CONFIG_DRM_LVDS_CODEC is not set +# CONFIG_DRM_MALI_DISPLAY is not set +# CONFIG_DRM_MCDE is not set +# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set +# CONFIG_DRM_MGAG200 is not set +# CONFIG_DRM_MXSFB is not set +# CONFIG_DRM_NOUVEAU is not set +# CONFIG_DRM_NWL_MIPI_DSI is not set +# CONFIG_DRM_NXP_PTN3460 is not set +# CONFIG_DRM_OFDRM is not set +# CONFIG_DRM_OMAP is not set +# CONFIG_DRM_PANEL_ABT_Y030XX067A is not set +# CONFIG_DRM_PANEL_ARM_VERSATILE is not set +# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set +# CONFIG_DRM_PANEL_AUO_A030JTN01 is not set +# CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0 is not set +# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set +# CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A is not set +# CONFIG_DRM_PANEL_BOE_TV101WUM_LL2 is not set +# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set +# CONFIG_DRM_PANEL_DSI_CM is not set +# CONFIG_DRM_PANEL_EBBG_FT8719 is not set +# CONFIG_DRM_PANEL_EDP is not set +# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set +# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set +# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set +# CONFIG_DRM_PANEL_HIMAX_HX83102 is not set +# CONFIG_DRM_PANEL_HIMAX_HX83112A is not set +# CONFIG_DRM_PANEL_HIMAX_HX8394 is not set +# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9341 is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9805 is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9806E is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9882T is not set +# CONFIG_DRM_PANEL_INNOLUX_EJ030NA is not set +# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set +# CONFIG_DRM_PANEL_JADARD_JD9365DA_H3 is not set +# CONFIG_DRM_PANEL_JDI_LPM102A188A is not set +# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set +# CONFIG_DRM_PANEL_JDI_R63452 is not set +# CONFIG_DRM_PANEL_KHADAS_TS050 is not set +# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set +# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set +# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set +# CONFIG_DRM_PANEL_LG_LB035Q02 is not set +# CONFIG_DRM_PANEL_LG_LG4573 is not set +# CONFIG_DRM_PANEL_LG_SW43408 is not set +# CONFIG_DRM_PANEL_LINCOLNTECH_LCD197 is not set +# CONFIG_DRM_PANEL_LVDS is not set +# CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966 is not set +# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set +# CONFIG_DRM_PANEL_MIPI_DBI is not set +# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set +# CONFIG_DRM_PANEL_NEWVISION_NV3051D is not set +# CONFIG_DRM_PANEL_NEWVISION_NV3052C is not set +# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set +# CONFIG_DRM_PANEL_NOVATEK_NT35560 is not set +# CONFIG_DRM_PANEL_NOVATEK_NT35950 is not set +# CONFIG_DRM_PANEL_NOVATEK_NT36523 is not set +# CONFIG_DRM_PANEL_NOVATEK_NT36672A is not set +# CONFIG_DRM_PANEL_NOVATEK_NT36672E is not set +# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set +# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set +# CONFIG_DRM_PANEL_ORISETECH_OTA5601A is not set +# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set +# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set +# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set +# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set +# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set +# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set +# CONFIG_DRM_PANEL_RAYDIUM_RM692E5 is not set +# CONFIG_DRM_PANEL_RAYDIUM_RM69380 is not set +# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set +# CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20 is not set +# CONFIG_DRM_PANEL_SAMSUNG_DB7430 is not set +# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6D27A1 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6D7AA0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E3FA7 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_SOFEF00 is not set +# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set +# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set +# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set +# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set +# CONFIG_DRM_PANEL_SHARP_LS060T1SX01 is not set +# CONFIG_DRM_PANEL_SIMPLE is not set +# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set +# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set +# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set +# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set +# CONFIG_DRM_PANEL_SONY_TD4353_JDI is not set +# CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521 is not set +# CONFIG_DRM_PANEL_STARTEK_KD070FHFID015 is not set +# CONFIG_DRM_PANEL_SYNAPTICS_R63353 is not set +# CONFIG_DRM_PANEL_TDO_TL070WSH30 is not set +# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set +# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set +# CONFIG_DRM_PANEL_TPO_TPG110 is not set +# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set +# CONFIG_DRM_PANEL_VISIONOX_R66451 is not set +# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set +# CONFIG_DRM_PANEL_VISIONOX_VTDR6130 is not set +# CONFIG_DRM_PANEL_WIDECHIPS_WS2401 is not set +# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set +# CONFIG_DRM_PANFROST is not set +# CONFIG_DRM_PANIC is not set +# CONFIG_DRM_PANTHOR is not set +# CONFIG_DRM_PARADE_PS8622 is not set +# CONFIG_DRM_PARADE_PS8640 is not set +# CONFIG_DRM_PL111 is not set +# CONFIG_DRM_POWERVR is not set +# CONFIG_DRM_QXL is not set +# CONFIG_DRM_RADEON is not set +# CONFIG_DRM_RADEON_USERPTR is not set +# CONFIG_DRM_RCAR_DW_HDMI is not set +# CONFIG_DRM_RCAR_LVDS is not set +# CONFIG_DRM_RCAR_USE_LVDS is not set +# CONFIG_DRM_RCAR_USE_MIPI_DSI is not set +# CONFIG_DRM_ROCKCHIP is not set +# CONFIG_DRM_SAMSUNG_DSIM is not set +# CONFIG_DRM_SII902X is not set +# CONFIG_DRM_SII9234 is not set +# CONFIG_DRM_SIL_SII8620 is not set +# CONFIG_DRM_SIMPLEDRM is not set +# CONFIG_DRM_SIMPLE_BRIDGE is not set +# CONFIG_DRM_SSD130X is not set +# CONFIG_DRM_STI is not set +# CONFIG_DRM_STM is not set +# CONFIG_DRM_SUN4I is not set +# CONFIG_DRM_THINE_THC63LVD1024 is not set +# CONFIG_DRM_TIDSS is not set +# CONFIG_DRM_TILCDC is not set +# CONFIG_DRM_TI_DLPC3433 is not set +# CONFIG_DRM_TI_SN65DSI83 is not set +# CONFIG_DRM_TI_SN65DSI86 is not set +# CONFIG_DRM_TI_TFP410 is not set +# CONFIG_DRM_TI_TPD12S015 is not set +# CONFIG_DRM_TOSHIBA_TC358762 is not set +# CONFIG_DRM_TOSHIBA_TC358764 is not set +# CONFIG_DRM_TOSHIBA_TC358767 is not set +# CONFIG_DRM_TOSHIBA_TC358768 is not set +# CONFIG_DRM_TOSHIBA_TC358775 is not set +# CONFIG_DRM_TVE200 is not set +# CONFIG_DRM_UDL is not set +# CONFIG_DRM_V3D is not set +# CONFIG_DRM_VBOXVIDEO is not set +# CONFIG_DRM_VC4_HDMI_CEC is not set +# CONFIG_DRM_VGEM is not set +# CONFIG_DRM_VIRTIO_GPU is not set +# CONFIG_DRM_VKMS is not set +# CONFIG_DRM_VMWGFX is not set +# CONFIG_DRM_WERROR is not set +# CONFIG_DRM_XE is not set +# CONFIG_DRM_XEN is not set +# CONFIG_DRM_XEN_FRONTEND is not set +# CONFIG_DS1682 is not set +# CONFIG_DS1803 is not set +# CONFIG_DS4424 is not set +# CONFIG_DST_CACHE is not set +# CONFIG_DTLK is not set +# CONFIG_DUMMY is not set +CONFIG_DUMMY_CONSOLE_COLUMNS=80 +CONFIG_DUMMY_CONSOLE_ROWS=25 +# CONFIG_DUMMY_IRQ is not set +# CONFIG_DVB_A8293 is not set +# CONFIG_DVB_AF9013 is not set +# CONFIG_DVB_AF9033 is not set +# CONFIG_DVB_AS102 is not set +# CONFIG_DVB_ASCOT2E is not set +# CONFIG_DVB_ATBM8830 is not set +# CONFIG_DVB_AU8522_DTV is not set +# CONFIG_DVB_AU8522_V4L is not set +# CONFIG_DVB_B2C2_FLEXCOP_PCI is not set +# CONFIG_DVB_B2C2_FLEXCOP_USB is not set +# CONFIG_DVB_BCM3510 is not set +# CONFIG_DVB_CORE is not set +# CONFIG_DVB_CX22700 is not set +# CONFIG_DVB_CX22702 is not set +# CONFIG_DVB_CX24110 is not set +# CONFIG_DVB_CX24116 is not set +# CONFIG_DVB_CX24117 is not set +# CONFIG_DVB_CX24120 is not set +# CONFIG_DVB_CX24123 is not set +# CONFIG_DVB_CXD2099 is not set +# CONFIG_DVB_CXD2820R is not set +# CONFIG_DVB_CXD2841ER is not set +# CONFIG_DVB_CXD2880 is not set +# CONFIG_DVB_DDBRIDGE is not set +# CONFIG_DVB_DEMUX_SECTION_LOSS_LOG is not set +# CONFIG_DVB_DIB3000MB is not set +# CONFIG_DVB_DIB3000MC is not set +# CONFIG_DVB_DIB7000M is not set +# CONFIG_DVB_DIB7000P is not set +# CONFIG_DVB_DIB8000 is not set +# CONFIG_DVB_DIB9000 is not set +# CONFIG_DVB_DRX39XYJ is not set +# CONFIG_DVB_DRXD is not set +# CONFIG_DVB_DRXK is not set +# CONFIG_DVB_DS3000 is not set +# CONFIG_DVB_DUMMY_FE is not set +# CONFIG_DVB_DYNAMIC_MINORS is not set +# CONFIG_DVB_EC100 is not set +# CONFIG_DVB_FIREDTV is not set +# CONFIG_DVB_HELENE is not set +# CONFIG_DVB_HORUS3A is not set +# CONFIG_DVB_ISL6405 is not set +# CONFIG_DVB_ISL6421 is not set +# CONFIG_DVB_ISL6423 is not set +# CONFIG_DVB_IX2505V is not set +# CONFIG_DVB_L64781 is not set +# CONFIG_DVB_LG2160 is not set +# CONFIG_DVB_LGDT3305 is not set +# CONFIG_DVB_LGDT3306A is not set +# CONFIG_DVB_LGDT330X is not set +# CONFIG_DVB_LGS8GL5 is not set +# CONFIG_DVB_LGS8GXX is not set +# CONFIG_DVB_LNBH25 is not set +# CONFIG_DVB_LNBH29 is not set +# CONFIG_DVB_LNBP21 is not set +# CONFIG_DVB_LNBP22 is not set +# CONFIG_DVB_M88DS3103 is not set +# CONFIG_DVB_M88RS2000 is not set +CONFIG_DVB_MAX_ADAPTERS=16 +# CONFIG_DVB_MB86A16 is not set +# CONFIG_DVB_MB86A20S is not set +# CONFIG_DVB_MMAP is not set +# CONFIG_DVB_MN88443X is not set +# CONFIG_DVB_MN88472 is not set +# CONFIG_DVB_MN88473 is not set +# CONFIG_DVB_MT312 is not set +# CONFIG_DVB_MT352 is not set +# CONFIG_DVB_MXL5XX is not set +# CONFIG_DVB_MXL692 is not set +# CONFIG_DVB_NET is not set +# CONFIG_DVB_NETUP_UNIDVB is not set +# CONFIG_DVB_NGENE is not set +# CONFIG_DVB_NXT200X is not set +# CONFIG_DVB_NXT6000 is not set +# CONFIG_DVB_OR51132 is not set +# CONFIG_DVB_OR51211 is not set +# CONFIG_DVB_PLATFORM_DRIVERS is not set +# CONFIG_DVB_PLL is not set +# CONFIG_DVB_PLUTO2 is not set +# CONFIG_DVB_PT1 is not set +# CONFIG_DVB_PT3 is not set +# CONFIG_DVB_RTL2830 is not set +# CONFIG_DVB_RTL2832 is not set +# CONFIG_DVB_RTL2832_SDR is not set +# CONFIG_DVB_S5H1409 is not set +# CONFIG_DVB_S5H1411 is not set +# CONFIG_DVB_S5H1420 is not set +# CONFIG_DVB_S5H1432 is not set +# CONFIG_DVB_S921 is not set +# CONFIG_DVB_SI2165 is not set +# CONFIG_DVB_SI2168 is not set +# CONFIG_DVB_SI21XX is not set +# CONFIG_DVB_SP2 is not set +# CONFIG_DVB_SP8870 is not set +# CONFIG_DVB_SP887X is not set +# CONFIG_DVB_STB0899 is not set +# CONFIG_DVB_STB6000 is not set +# CONFIG_DVB_STB6100 is not set +# CONFIG_DVB_STV0288 is not set +# CONFIG_DVB_STV0297 is not set +# CONFIG_DVB_STV0299 is not set +# CONFIG_DVB_STV0367 is not set +# CONFIG_DVB_STV0900 is not set +# CONFIG_DVB_STV090x is not set +# CONFIG_DVB_STV0910 is not set +# CONFIG_DVB_STV6110 is not set +# CONFIG_DVB_STV6110x is not set +# CONFIG_DVB_STV6111 is not set +# CONFIG_DVB_TC90522 is not set +# CONFIG_DVB_TDA10021 is not set +# CONFIG_DVB_TDA10023 is not set +# CONFIG_DVB_TDA10048 is not set +# CONFIG_DVB_TDA1004X is not set +# CONFIG_DVB_TDA10071 is not set +# CONFIG_DVB_TDA10086 is not set +# CONFIG_DVB_TDA18271C2DD is not set +# CONFIG_DVB_TDA665x is not set +# CONFIG_DVB_TDA8083 is not set +# CONFIG_DVB_TDA8261 is not set +# CONFIG_DVB_TDA826X is not set +# CONFIG_DVB_TEST_DRIVERS is not set +# CONFIG_DVB_TS2020 is not set +# CONFIG_DVB_TTUSB_BUDGET is not set +# CONFIG_DVB_TTUSB_DEC is not set +# CONFIG_DVB_TUA6100 is not set +# CONFIG_DVB_TUNER_CX24113 is not set +# CONFIG_DVB_TUNER_DIB0070 is not set +# CONFIG_DVB_TUNER_DIB0090 is not set +# CONFIG_DVB_TUNER_ITD1000 is not set +# CONFIG_DVB_ULE_DEBUG is not set +# CONFIG_DVB_USB is not set +# CONFIG_DVB_USB_V2 is not set +# CONFIG_DVB_VES1820 is not set +# CONFIG_DVB_VES1X93 is not set +# CONFIG_DVB_ZD1301_DEMOD is not set +# CONFIG_DVB_ZL10036 is not set +# CONFIG_DVB_ZL10039 is not set +# CONFIG_DVB_ZL10353 is not set +# CONFIG_DWC_PCIE_PMU is not set +# CONFIG_DWC_XLGMAC is not set +# CONFIG_DWMAC_DWC_QOS_ETH is not set +# CONFIG_DWMAC_INTEL_PLAT is not set +# CONFIG_DWMAC_IPQ806X is not set +# CONFIG_DWMAC_LOONGSON is not set +# CONFIG_DWMAC_LPC18XX is not set +# CONFIG_DWMAC_MESON is not set +# CONFIG_DWMAC_ROCKCHIP is not set +# CONFIG_DWMAC_SOCFPGA is not set +# CONFIG_DWMAC_STI is not set +# CONFIG_DW_AXI_DMAC is not set +# CONFIG_DW_DMAC is not set +# CONFIG_DW_DMAC_PCI is not set +# CONFIG_DW_EDMA is not set +# CONFIG_DW_EDMA_PCIE is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_DW_XDATA_PCIE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DYNAMIC_DEBUG_CORE is not set +# CONFIG_E100 is not set +# CONFIG_E1000 is not set +# CONFIG_E1000E is not set +# CONFIG_E1000E_HWTS is not set +# CONFIG_EARLY_PRINTK_8250 is not set +# CONFIG_EARLY_PRINTK_USB_XDBC is not set +# CONFIG_EBC_C384_WDT is not set +# CONFIG_ECHO is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_EC_LENOVO_YOGA_C630 is not set +# CONFIG_EDAC is not set +# CONFIG_EEEPC_LAPTOP is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_DIGSY_MTC_CFG is not set +# CONFIG_EEPROM_EE1004 is not set +# CONFIG_EEPROM_IDT_89HPESX is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EFI is not set +CONFIG_EFI_PARTITION=y +# CONFIG_EFI_VARS_PSTORE is not set +# CONFIG_EFS_FS is not set +CONFIG_ELFCORE=y +# CONFIG_ELF_CORE is not set +# CONFIG_EMAC_ROCKCHIP is not set +# CONFIG_EM_TIMER_STI is not set +# CONFIG_ENA_ETHERNET is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_ENCRYPTED_KEYS is not set +# CONFIG_ENCX24J600 is not set +# CONFIG_ENERGY_MODEL is not set +# CONFIG_ENIC is not set +# CONFIG_ENS160 is not set +# CONFIG_ENS210 is not set +# CONFIG_ENVELOPE_DETECTOR is not set +# CONFIG_EPAPR_PARAVIRT is not set +# CONFIG_EPIC100 is not set +CONFIG_EPOLL=y +# CONFIG_EQUALIZER is not set +# CONFIG_EROFS_FS is not set +# CONFIG_EROFS_FS_BACKED_BY_FILE is not set +# CONFIG_EROFS_FS_DEBUG is not set +# CONFIG_EROFS_FS_ONDEMAND is not set +CONFIG_EROFS_FS_PCPU_KTHREAD=y +CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI=y +# CONFIG_EROFS_FS_POSIX_ACL is not set +# CONFIG_EROFS_FS_SECURITY is not set +# CONFIG_EROFS_FS_XATTR is not set +# CONFIG_EROFS_FS_ZIP is not set +# CONFIG_EROFS_FS_ZIP_DEFLATE is not set +# CONFIG_EROFS_FS_ZIP_ZSTD is not set +# CONFIG_ET131X is not set +CONFIG_ETHERNET=y +# CONFIG_ETHOC is not set +CONFIG_ETHTOOL_NETLINK=y +CONFIG_EVENTFD=y +# CONFIG_EVM is not set +CONFIG_EXECMEM=y +# CONFIG_EXFAT_FS is not set +CONFIG_EXPERT=y +CONFIG_EXPORTFS=y +# CONFIG_EXPORTFS_BLOCK_OPS is not set +# CONFIG_EXT2_FS is not set +CONFIG_EXT2_FS_XATTR=y +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_DEBUG is not set +# CONFIG_EXT4_FS is not set +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +CONFIG_EXT4_USE_FOR_EXT2=y +# CONFIG_EXTCON is not set +# CONFIG_EXTCON_ADC_JACK is not set +# CONFIG_EXTCON_AXP288 is not set +# CONFIG_EXTCON_FSA9480 is not set +# CONFIG_EXTCON_GPIO is not set +# CONFIG_EXTCON_INTEL_INT3496 is not set +# CONFIG_EXTCON_LC824206XA is not set +# CONFIG_EXTCON_MAX3355 is not set +# CONFIG_EXTCON_PTN5150 is not set +# CONFIG_EXTCON_QCOM_SPMI_MISC is not set +# CONFIG_EXTCON_RT8973A is not set +# CONFIG_EXTCON_SM5502 is not set +# CONFIG_EXTCON_USBC_TUSB320 is not set +# CONFIG_EXTCON_USB_GPIO is not set +CONFIG_EXTRA_FIRMWARE="" +CONFIG_EXTRA_TARGETS="" +# CONFIG_EXYNOS_ADC is not set +# CONFIG_EYEQ is not set +# CONFIG_EZCHIP_NPS_MANAGEMENT_ENET is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_F2FS_CHECK_FS is not set +# CONFIG_F2FS_FAULT_INJECTION is not set +# CONFIG_F2FS_FS is not set +# CONFIG_F2FS_FS_COMPRESSION is not set +# CONFIG_F2FS_FS_POSIX_ACL is not set +# CONFIG_F2FS_FS_SECURITY is not set +CONFIG_F2FS_FS_XATTR=y +# CONFIG_F2FS_IOSTAT is not set +CONFIG_F2FS_STAT_FS=y +# CONFIG_F2FS_UNFAIR_RWSEM is not set +# CONFIG_FAILOVER is not set +# CONFIG_FAIR_GROUP_SCHED is not set +# CONFIG_FANOTIFY is not set +# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_FAT_DEFAULT_UTF8 is not set +# CONFIG_FAT_FS is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_FB is not set +# CONFIG_FBNIC is not set +# CONFIG_FB_3DFX is not set +# CONFIG_FB_ARC is not set +# CONFIG_FB_ARK is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_ATMEL is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_ATY128 is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_BIG_ENDIAN is not set +# CONFIG_FB_BOTH_ENDIAN is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_CARMINE is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_CIRRUS is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_DEVICE is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_FSL_DIU is not set +# CONFIG_FB_GEODE is not set +# CONFIG_FB_GOLDFISH is not set +# CONFIG_FB_HGA is not set +# CONFIG_FB_I740 is not set +# CONFIG_FB_IBM_GXT4500 is not set +# CONFIG_FB_IMSTT is not set +# CONFIG_FB_IMX is not set +# CONFIG_FB_KYRO is not set +# CONFIG_FB_LITTLE_ENDIAN is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_MATROX is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_N411 is not set +# CONFIG_FB_NEOMAGIC is not set +CONFIG_FB_NOTIFY=y +# CONFIG_FB_NVIDIA is not set +# CONFIG_FB_OF is not set +# CONFIG_FB_OMAP2 is not set +# CONFIG_FB_OPENCORES is not set +# CONFIG_FB_PM2 is not set +# CONFIG_FB_PM3 is not set +# CONFIG_FB_PS3 is not set +# CONFIG_FB_PXA is not set +# CONFIG_FB_RADEON is not set +# CONFIG_FB_RIVA is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_S3 is not set +# CONFIG_FB_SAVAGE is not set +# CONFIG_FB_SIMPLE is not set +# CONFIG_FB_SIS is not set +# CONFIG_FB_SM712 is not set +# CONFIG_FB_SM750 is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_SSD1307 is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_TFT is not set +# CONFIG_FB_TFT_AGM1264K_FL is not set +# CONFIG_FB_TFT_BD663474 is not set +# CONFIG_FB_TFT_HX8340BN is not set +# CONFIG_FB_TFT_HX8347D is not set +# CONFIG_FB_TFT_HX8353D is not set +# CONFIG_FB_TFT_HX8357D is not set +# CONFIG_FB_TFT_ILI9163 is not set +# CONFIG_FB_TFT_ILI9320 is not set +# CONFIG_FB_TFT_ILI9325 is not set +# CONFIG_FB_TFT_ILI9340 is not set +# CONFIG_FB_TFT_ILI9341 is not set +# CONFIG_FB_TFT_ILI9481 is not set +# CONFIG_FB_TFT_ILI9486 is not set +# CONFIG_FB_TFT_PCD8544 is not set +# CONFIG_FB_TFT_RA8875 is not set +# CONFIG_FB_TFT_S6D02A1 is not set +# CONFIG_FB_TFT_S6D1121 is not set +# CONFIG_FB_TFT_SEPS525 is not set +# CONFIG_FB_TFT_SH1106 is not set +# CONFIG_FB_TFT_SSD1289 is not set +# CONFIG_FB_TFT_SSD1305 is not set +# CONFIG_FB_TFT_SSD1306 is not set +# CONFIG_FB_TFT_SSD1331 is not set +# CONFIG_FB_TFT_SSD1351 is not set +# CONFIG_FB_TFT_ST7735R is not set +# CONFIG_FB_TFT_ST7789V is not set +# CONFIG_FB_TFT_TINYLCD is not set +# CONFIG_FB_TFT_TLS8204 is not set +# CONFIG_FB_TFT_UC1611 is not set +# CONFIG_FB_TFT_UC1701 is not set +# CONFIG_FB_TFT_UPD161704 is not set +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_TRIDENT is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_UVESA is not set +# CONFIG_FB_VGA16 is not set +# CONFIG_FB_VIA is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_VOODOO1 is not set +# CONFIG_FB_VT8623 is not set +# CONFIG_FCOE is not set +# CONFIG_FCOE_FNIC is not set +# CONFIG_FDDI is not set +# CONFIG_FEALNX is not set +# CONFIG_FHANDLE is not set +CONFIG_FIB_RULES=y +# CONFIG_FIELDBUS_DEV is not set +CONFIG_FILE_LOCKING=y +# CONFIG_FIND_BIT_BENCHMARK is not set +# CONFIG_FIREWIRE is not set +# CONFIG_FIREWIRE_NOSY is not set +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FIRMWARE_MEMMAP is not set +# CONFIG_FIXED_PHY is not set +CONFIG_FLATMEM=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_FM10K is not set +# CONFIG_FONTS is not set +# CONFIG_FONT_6x8 is not set +# CONFIG_FONT_TER16x32 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_FORCE_NR_CPUS is not set +CONFIG_FORTIFY_SOURCE=y +# CONFIG_FPGA is not set +# CONFIG_FPROBE is not set +# CONFIG_FRAMEBUFFER_CONSOLE is not set +# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set +# CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set +# CONFIG_FRAME_POINTER is not set +# CONFIG_FREEZER is not set +# CONFIG_FSCACHE is not set +# CONFIG_FSI is not set +# CONFIG_FSL_DPAA2_SWITCH is not set +# CONFIG_FSL_EDMA is not set +# CONFIG_FSL_ENETC is not set +# CONFIG_FSL_ENETC_IERB is not set +# CONFIG_FSL_ENETC_MDIO is not set +# CONFIG_FSL_ENETC_VF is not set +# CONFIG_FSL_ERRATUM_A008585 is not set +# CONFIG_FSL_MC_BUS is not set +# CONFIG_FSL_PQ_MDIO is not set +# CONFIG_FSL_QDMA is not set +# CONFIG_FSL_RCPM is not set +# CONFIG_FSL_XGMAC_MDIO is not set +CONFIG_FSNOTIFY=y +# CONFIG_FS_DAX is not set +# CONFIG_FS_ENCRYPTION is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FS_STACK=y +# CONFIG_FS_VERITY is not set +# CONFIG_FTGMAC100 is not set +# CONFIG_FTL is not set +# CONFIG_FTMAC100 is not set +# CONFIG_FTRACE is not set +# CONFIG_FTRACE_RECORD_RECURSION is not set +# CONFIG_FTRACE_SORT_STARTUP_TEST is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_FTRACE_VALIDATE_RCU_IS_WATCHING is not set +# CONFIG_FTR_FIXUP_SELFTEST is not set +# CONFIG_FTWDT010_WATCHDOG is not set +# CONFIG_FUEL_GAUGE_MM8013 is not set +# CONFIG_FUJITSU_ERRATUM_010001 is not set +# CONFIG_FUJITSU_ES is not set +# CONFIG_FUJITSU_LAPTOP is not set +# CONFIG_FUJITSU_TABLET is not set +# CONFIG_FUNCTION_ERROR_INJECTION is not set +# CONFIG_FUNCTION_GRAPH_RETVAL is not set +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_FUN_ETH is not set +# CONFIG_FUSE_FS is not set +# CONFIG_FUSE_PASSTHROUGH is not set +# CONFIG_FUSION is not set +# CONFIG_FUSION_FC is not set +# CONFIG_FUSION_SAS is not set +# CONFIG_FUSION_SPI is not set +CONFIG_FUTEX=y +CONFIG_FUTEX_PI=y +# CONFIG_FW_CFG_SYSFS is not set +# CONFIG_FW_DEVLINK_SYNC_STATE_TIMEOUT is not set +CONFIG_FW_LOADER=y +# CONFIG_FW_LOADER_COMPRESS is not set +# CONFIG_FW_LOADER_DEBUG is not set +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +# CONFIG_FW_UPLOAD is not set +# CONFIG_FXAS21002C is not set +# CONFIG_FXLS8962AF_I2C is not set +# CONFIG_FXLS8962AF_SPI is not set +# CONFIG_FXOS8700_I2C is not set +# CONFIG_FXOS8700_SPI is not set +CONFIG_GACT_PROB=y +# CONFIG_GADGET_UAC1 is not set +# CONFIG_GAMEPORT is not set +CONFIG_GCC_NO_STRINGOP_OVERFLOW=y +# CONFIG_GCC_PLUGINS is not set +# CONFIG_GCOV is not set +# CONFIG_GCOV_KERNEL is not set +# CONFIG_GDB_SCRIPTS is not set +# CONFIG_GEMINI_ETHERNET is not set +# CONFIG_GENERIC_ADC_BATTERY is not set +# CONFIG_GENERIC_ADC_THERMAL is not set +CONFIG_GENERIC_CALIBRATE_DELAY=y +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_GENERIC_HWEIGHT=y +# CONFIG_GENERIC_IRQ_DEBUGFS is not set +CONFIG_GENERIC_IRQ_IPI=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_NET_UTILS=y +# CONFIG_GENERIC_PHY is not set +CONFIG_GENERIC_PTDUMP=y +CONFIG_GENERIC_VDSO_TIME_NS=y +# CONFIG_GENEVE is not set +# CONFIG_GENWQE is not set +# CONFIG_GFS2_FS is not set +# CONFIG_GIGABYTE_WMI is not set +# CONFIG_GLOB_SELFTEST is not set +# CONFIG_GNSS is not set +# CONFIG_GOLDFISH is not set +# CONFIG_GOOGLE_CBMEM is not set +# CONFIG_GOOGLE_FIRMWARE is not set +# CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT is not set +# CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY is not set +# CONFIG_GOOGLE_SMI is not set +# CONFIG_GP2AP002 is not set +# CONFIG_GP2AP020A00F is not set +# CONFIG_GPD_POCKET_FAN is not set +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_FASTPATH_LIMIT=512 +# CONFIG_GPIO_104_DIO_48E is not set +# CONFIG_GPIO_104_IDIO_16 is not set +# CONFIG_GPIO_104_IDI_48 is not set +# CONFIG_GPIO_74X164 is not set +# CONFIG_GPIO_74XX_MMIO is not set +# CONFIG_GPIO_ADNP is not set +# CONFIG_GPIO_AGGREGATOR is not set +# CONFIG_GPIO_ALTERA is not set +# CONFIG_GPIO_AMD8111 is not set +# CONFIG_GPIO_AMDPT is not set +# CONFIG_GPIO_AMD_FCH is not set +# CONFIG_GPIO_BCM_KONA is not set +# CONFIG_GPIO_BRCMSTB is not set +# CONFIG_GPIO_BT8XX is not set +# CONFIG_GPIO_CADENCE is not set +# CONFIG_GPIO_CDEV is not set +# CONFIG_GPIO_CDEV_V1 is not set +# CONFIG_GPIO_CS5535 is not set +# CONFIG_GPIO_DS4520 is not set +# CONFIG_GPIO_DWAPB is not set +# CONFIG_GPIO_EM is not set +# CONFIG_GPIO_EXAR is not set +# CONFIG_GPIO_F7188X is not set +# CONFIG_GPIO_FTGPIO010 is not set +# CONFIG_GPIO_FXL6408 is not set +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_GPIO_MM is not set +# CONFIG_GPIO_GRGPIO is not set +# CONFIG_GPIO_GW_PLD is not set +# CONFIG_GPIO_HISI is not set +# CONFIG_GPIO_HLWD is not set +# CONFIG_GPIO_ICH is not set +# CONFIG_GPIO_IT87 is not set +# CONFIG_GPIO_LATCH is not set +# CONFIG_GPIO_LOGICVC is not set +# CONFIG_GPIO_LINE_MUX is not set +# CONFIG_GPIO_MAX3191X is not set +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_MB86S7X is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_ML_IOH is not set +# CONFIG_GPIO_MOCKUP is not set +# CONFIG_GPIO_MPC8XXX is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCA953X_IRQ is not set +# CONFIG_GPIO_PCA9570 is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_PCH is not set +# CONFIG_GPIO_PCIE_IDIO_24 is not set +# CONFIG_GPIO_PCI_IDIO_16 is not set +# CONFIG_GPIO_PISOSR is not set +# CONFIG_GPIO_PL061 is not set +# CONFIG_GPIO_RCAR is not set +# CONFIG_GPIO_RDC321X is not set +# CONFIG_GPIO_SAMA5D2_PIOBU is not set +# CONFIG_GPIO_SCH is not set +# CONFIG_GPIO_SCH311X is not set +# CONFIG_GPIO_SIFIVE is not set +# CONFIG_GPIO_SIM is not set +# CONFIG_GPIO_SLOPPY_LOGIC_ANALYZER is not set +# CONFIG_GPIO_SYSCON is not set +CONFIG_GPIO_SYSFS=y +# CONFIG_GPIO_TPIC2810 is not set +# CONFIG_GPIO_TS4900 is not set +# CONFIG_GPIO_TS5500 is not set +# CONFIG_GPIO_VIRTIO is not set +# CONFIG_GPIO_VIRTUSER is not set +# CONFIG_GPIO_VX855 is not set +# CONFIG_GPIO_WATCHDOG is not set +# CONFIG_GPIO_WINBOND is not set +# CONFIG_GPIO_WS16C48 is not set +# CONFIG_GPIO_XGENE is not set +# CONFIG_GPIO_XILINX is not set +# CONFIG_GPIO_XRA1403 is not set +# CONFIG_GPIO_ZEVIO is not set +# CONFIG_GP_PCI1XXXX is not set +# CONFIG_GREENASIA_FF is not set +# CONFIG_GREYBUS is not set +# CONFIG_GTP is not set +# CONFIG_GUP_TEST is not set +# CONFIG_GVE is not set +# CONFIG_HAMACHI is not set +# CONFIG_HAMRADIO is not set +# CONFIG_HAPPYMEAL is not set +CONFIG_HARDENED_USERCOPY=y +CONFIG_HARDEN_BRANCH_HISTORY=y +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_HAVE_ARM_ARCH_TIMER is not set +# CONFIG_HCALL_STATS is not set +# CONFIG_HDC100X is not set +# CONFIG_HDC2010 is not set +# CONFIG_HDC3020 is not set +# CONFIG_HDLC is not set +# CONFIG_HDLC_CISCO is not set +# CONFIG_HDLC_FR is not set +# CONFIG_HDLC_PPP is not set +# CONFIG_HDLC_RAW is not set +# CONFIG_HDLC_RAW_ETH is not set +# CONFIG_HDMI_LPE_AUDIO is not set +# CONFIG_HDQ_MASTER_OMAP is not set +# CONFIG_HEADERS_INSTALL is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HI6421V600_IRQ is not set +# CONFIG_HI8435 is not set +# CONFIG_HIBERNATION is not set +# CONFIG_HIBERNATION_COMP_LZ4 is not set +# CONFIG_HID is not set +# CONFIG_HIDRAW is not set +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACCUTOUCH is not set +# CONFIG_HID_ACRUX is not set +# CONFIG_HID_ACRUX_FF is not set +# CONFIG_HID_ALPS is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_APPLEIR is not set +# CONFIG_HID_ASUS is not set +# CONFIG_HID_AUREAL is not set +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_BETOP_FF is not set +# CONFIG_HID_BIGBEN_FF is not set +# CONFIG_HID_BPF is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_CMEDIA is not set +# CONFIG_HID_CORSAIR is not set +# CONFIG_HID_COUGAR is not set +# CONFIG_HID_CP2112 is not set +# CONFIG_HID_CREATIVE_SB0540 is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_ELAN is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_ELO is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_EVISION is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_FT260 is not set +# CONFIG_HID_GEMBIRD is not set +# CONFIG_HID_GENERIC is not set +# CONFIG_HID_GFRM is not set +# CONFIG_HID_GLORIOUS is not set +# CONFIG_HID_GOODIX_SPI is not set +# CONFIG_HID_GOOGLE_HAMMER is not set +# CONFIG_HID_GOOGLE_STADIA_FF is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_GT683R is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_ITE is not set +# CONFIG_HID_JABRA is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LED is not set +# CONFIG_HID_LENOVO is not set +# CONFIG_HID_LETSKETCH is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_LOGITECH_DJ is not set +# CONFIG_HID_LOGITECH_HIDPP is not set +# CONFIG_HID_MACALLY is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_MALTRON is not set +# CONFIG_HID_MAYFLASH is not set +# CONFIG_HID_MCP2200 is not set +# CONFIG_HID_MCP2221 is not set +# CONFIG_HID_MEGAWORLD_FF is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NINTENDO is not set +# CONFIG_HID_NTI is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_NVIDIA_SHIELD is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PENMOUNT is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PID is not set +# CONFIG_HID_PLANTRONICS is not set +# CONFIG_HID_PLAYSTATION is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_PRODIKEYS is not set +# CONFIG_HID_PXRC is not set +# CONFIG_HID_RAZER is not set +# CONFIG_HID_REDRAGON is not set +# CONFIG_HID_RETRODE is not set +# CONFIG_HID_RMI is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SEMITEK is not set +# CONFIG_HID_SENSOR_HUB is not set +# CONFIG_HID_SIGMAMICRO is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEAM is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_HID_THINGM is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPRE is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_U2FZERO is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_UDRAW_PS3 is not set +# CONFIG_HID_VIEWSONIC is not set +# CONFIG_HID_VIVALDI is not set +# CONFIG_HID_VRC2 is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_WIIMOTE is not set +# CONFIG_HID_WINWING is not set +# CONFIG_HID_XIAOMI is not set +# CONFIG_HID_XINMO is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HIGHMEM is not set +CONFIG_HIGH_RES_TIMERS=y +# CONFIG_HINIC is not set +# CONFIG_HIP04_ETH is not set +# CONFIG_HIPPI is not set +# CONFIG_HISILICON_ERRATUM_161010101 is not set +# CONFIG_HISILICON_ERRATUM_161600802 is not set +# CONFIG_HISILICON_ERRATUM_162100801 is not set +# CONFIG_HISI_DMA is not set +# CONFIG_HISI_FEMAC is not set +# CONFIG_HISI_HIKEY_USB is not set +# CONFIG_HISI_PCIE_PMU is not set +# CONFIG_HISI_PTT is not set +# CONFIG_HIST_TRIGGERS_DEBUG is not set +# CONFIG_HIX5HD2_GMAC is not set +# CONFIG_HMC425 is not set +# CONFIG_HMC6352 is not set +# CONFIG_HNS is not set +# CONFIG_HNS3 is not set +# CONFIG_HNS3_PMU is not set +# CONFIG_HNS_DSAF is not set +# CONFIG_HNS_ENET is not set +# CONFIG_HOTPLUG_CPU is not set +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_HP03 is not set +# CONFIG_HP206C is not set +CONFIG_HPET_MMAP_DEFAULT=y +# CONFIG_HPFS_FS is not set +# CONFIG_HP_ILO is not set +# CONFIG_HP_WATCHDOG is not set +# CONFIG_HSA_AMD is not set +# CONFIG_HSC030PA is not set +# CONFIG_HSI is not set +# CONFIG_HSR is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTE is not set +# CONFIG_HTS221 is not set +# CONFIG_HTU21 is not set +# CONFIG_HUAWEI_WMI is not set +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON is not set +# CONFIG_HVC_DCC is not set +# CONFIG_HVC_UDBG is not set +# CONFIG_HWLAT_TRACER is not set +# CONFIG_HWMON is not set +# CONFIG_HWMON_DEBUG_CHIP is not set +# CONFIG_HWMON_VID is not set +# CONFIG_HWSPINLOCK is not set +# CONFIG_HWSPINLOCK_OMAP is not set +CONFIG_HW_PERF_EVENTS=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HW_RANDOM_AMD is not set +# CONFIG_HW_RANDOM_ARM_SMCCC_TRNG is not set +# CONFIG_HW_RANDOM_ATMEL is not set +# CONFIG_HW_RANDOM_BA431 is not set +# CONFIG_HW_RANDOM_BCM2835 is not set +# CONFIG_HW_RANDOM_CAVIUM is not set +# CONFIG_HW_RANDOM_CCTRNG is not set +# CONFIG_HW_RANDOM_CN10K is not set +# CONFIG_HW_RANDOM_EXYNOS is not set +# CONFIG_HW_RANDOM_GEODE is not set +# CONFIG_HW_RANDOM_INTEL is not set +# CONFIG_HW_RANDOM_IPROC_RNG200 is not set +# CONFIG_HW_RANDOM_MTK is not set +# CONFIG_HW_RANDOM_OMAP is not set +# CONFIG_HW_RANDOM_OMAP3_ROM is not set +# CONFIG_HW_RANDOM_PPC4XX is not set +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +CONFIG_HW_RANDOM_TPM=y +# CONFIG_HW_RANDOM_VIA is not set +# CONFIG_HW_RANDOM_VIRTIO is not set +# CONFIG_HW_RANDOM_XIPHERA is not set +# CONFIG_HX711 is not set +# CONFIG_HX9023S is not set +# CONFIG_HYPERV is not set +CONFIG_HZ=100 +CONFIG_HZ_100=y +# CONFIG_HZ_1000 is not set +# CONFIG_HZ_1024 is not set +# CONFIG_HZ_128 is not set +# CONFIG_HZ_200 is not set +# CONFIG_HZ_24 is not set +# CONFIG_HZ_250 is not set +# CONFIG_HZ_256 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_48 is not set +# CONFIG_HZ_500 is not set +# CONFIG_HZ_PERIODIC is not set +# CONFIG_I2C is not set +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCA is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set +# CONFIG_I2C_AU1550 is not set +# CONFIG_I2C_BCM2835 is not set +# CONFIG_I2C_BCM_IPROC is not set +# CONFIG_I2C_BRCMSTB is not set +# CONFIG_I2C_CADENCE is not set +# CONFIG_I2C_CBUS_GPIO is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_CP2615 is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEMUX_PINCTRL is not set +# CONFIG_I2C_DESIGNWARE_AMDPSP is not set +# CONFIG_I2C_DESIGNWARE_BAYTRAIL is not set +# CONFIG_I2C_DESIGNWARE_CORE is not set +# CONFIG_I2C_DESIGNWARE_PCI is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_DESIGNWARE_SLAVE is not set +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_EG20T is not set +# CONFIG_I2C_ELEKTOR is not set +# CONFIG_I2C_EMEV2 is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set +# CONFIG_I2C_HELPER_AUTO is not set +# CONFIG_I2C_HID is not set +# CONFIG_I2C_HID_OF is not set +# CONFIG_I2C_HID_OF_ELAN is not set +# CONFIG_I2C_HID_OF_GOODIX is not set +# CONFIG_I2C_HISI is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_IBM_IIC is not set +# CONFIG_I2C_IMG is not set +# CONFIG_I2C_ISCH is not set +# CONFIG_I2C_ISMT is not set +# CONFIG_I2C_JZ4780 is not set +# CONFIG_I2C_MLXCPLD is not set +# CONFIG_I2C_MPC is not set +# CONFIG_I2C_MT65XX is not set +# CONFIG_I2C_MUX is not set +# CONFIG_I2C_MUX_GPIO is not set +# CONFIG_I2C_MUX_GPMUX is not set +# CONFIG_I2C_MUX_LTC4306 is not set +# CONFIG_I2C_MUX_MLXCPLD is not set +# CONFIG_I2C_MUX_PCA9541 is not set +# CONFIG_I2C_MUX_PCA954x is not set +# CONFIG_I2C_MUX_PINCTRL is not set +# CONFIG_I2C_MUX_REG is not set +# CONFIG_I2C_MV64XXX is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_NOMADIK is not set +# CONFIG_I2C_NVIDIA_GPU is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_OCTEON is not set +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PCA_ISA is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PCI1XXXX is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_RCAR is not set +# CONFIG_I2C_RK3X is not set +# CONFIG_I2C_ROBOTFUZZ_OSIF is not set +# CONFIG_I2C_S3C2410 is not set +# CONFIG_I2C_SCMI is not set +# CONFIG_I2C_SH_MOBILE is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_SLAVE is not set +# CONFIG_I2C_SLAVE_EEPROM is not set +# CONFIG_I2C_SMBUS is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_THUNDERX is not set +# CONFIG_I2C_TINY_USB is not set +# CONFIG_I2C_VERSATILE is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VIRTIO is not set +# CONFIG_I2C_XILINX is not set +# CONFIG_I3C is not set +# CONFIG_I40E is not set +# CONFIG_I40EVF is not set +# CONFIG_I6300ESB_WDT is not set +# CONFIG_I82092 is not set +# CONFIG_I82365 is not set +# CONFIG_IAQCORE is not set +# CONFIG_IBM_ASM is not set +# CONFIG_IBM_EMAC_DEBUG is not set +# CONFIG_IBM_EMAC_EMAC4 is not set +# CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_EMAC_MAL_COMMON_ERR is not set +# CONFIG_IBM_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_EMAC_RGMII is not set +# CONFIG_IBM_EMAC_TAH is not set +# CONFIG_IBM_EMAC_ZMII is not set +# CONFIG_ICE is not set +# CONFIG_ICP10100 is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_ICS932S401 is not set +# CONFIG_IDEAPAD_LAPTOP is not set +# CONFIG_IDLE_PAGE_TRACKING is not set +# CONFIG_IDPF is not set +# CONFIG_IEEE802154 is not set +# CONFIG_IEEE802154_ADF7242 is not set +# CONFIG_IEEE802154_ATUSB is not set +# CONFIG_IEEE802154_CA8210 is not set +# CONFIG_IEEE802154_HWSIM is not set +# CONFIG_IEEE802154_MCR20A is not set +# CONFIG_IFB is not set +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set +# CONFIG_IGC is not set +# CONFIG_IIO is not set +# CONFIG_IIO_BUFFER is not set +# CONFIG_IIO_BUFFER_CB is not set +# CONFIG_IIO_BUFFER_DMA is not set +# CONFIG_IIO_BUFFER_DMAENGINE is not set +# CONFIG_IIO_BUFFER_HW_CONSUMER is not set +# CONFIG_IIO_CONFIGFS is not set +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 +# CONFIG_IIO_CROS_EC_ACCEL_LEGACY is not set +# CONFIG_IIO_INTERRUPT_TRIGGER is not set +# CONFIG_IIO_KX022A_I2C is not set +# CONFIG_IIO_KX022A_SPI is not set +# CONFIG_IIO_MUX is not set +# CONFIG_IIO_RESCALE is not set +# CONFIG_IIO_SIMPLE_DUMMY is not set +# CONFIG_IIO_SSP_SENSORHUB is not set +# CONFIG_IIO_ST_ACCEL_3AXIS is not set +# CONFIG_IIO_ST_GYRO_3AXIS is not set +# CONFIG_IIO_ST_LSM6DSX is not set +# CONFIG_IIO_ST_LSM9DS0 is not set +# CONFIG_IIO_ST_MAGN_3AXIS is not set +# CONFIG_IIO_ST_PRESS is not set +# CONFIG_IIO_SW_DEVICE is not set +# CONFIG_IIO_SW_TRIGGER is not set +# CONFIG_IIO_SYSFS_TRIGGER is not set +# CONFIG_IIO_TRIGGER is not set +# CONFIG_IIO_TRIGGERED_EVENT is not set +# CONFIG_IKCONFIG is not set +# CONFIG_IKCONFIG_PROC is not set +# CONFIG_IKHEADERS is not set +# CONFIG_IMA is not set +# CONFIG_IMGPDC_WDT is not set +# CONFIG_IMG_MDC_DMA is not set +# CONFIG_IMX7D_ADC is not set +# CONFIG_IMX8QXP_ADC is not set +# CONFIG_IMX93_ADC is not set +# CONFIG_IMX_IPUV3_CORE is not set +# CONFIG_IMX_SCMI_BBM_EXT is not set +# CONFIG_IMX_SCMI_MISC_DRV is not set +# CONFIG_IMX_THERMAL is not set +# CONFIG_INA2XX_ADC is not set +# CONFIG_INDIRECT_PIO is not set +CONFIG_INET=y +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_ESPINTCP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_DIAG is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_ESPINTCP is not set +# CONFIG_INET_IPCOMP is not set +CONFIG_INET_TABLE_PERTURB_ORDER=16 +# CONFIG_INET_TCP_DIAG is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_UDP_DIAG is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INFINIBAND is not set +# CONFIG_INFTL is not set +# CONFIG_INGENIC_ADC is not set +# CONFIG_INGENIC_CGU_JZ4725B is not set +# CONFIG_INGENIC_CGU_JZ4740 is not set +# CONFIG_INGENIC_CGU_JZ4755 is not set +# CONFIG_INGENIC_CGU_JZ4760 is not set +# CONFIG_INGENIC_CGU_JZ4770 is not set +# CONFIG_INGENIC_CGU_JZ4780 is not set +# CONFIG_INGENIC_CGU_X1000 is not set +# CONFIG_INGENIC_CGU_X1830 is not set +# CONFIG_INGENIC_OST is not set +# CONFIG_INGENIC_SYSOST is not set +# CONFIG_INGENIC_TCU_CLK is not set +# CONFIG_INGENIC_TCU_IRQ is not set +# CONFIG_INGENIC_TIMER is not set +# CONFIG_INITRAMFS_PRESERVE_MTIME is not set +CONFIG_INIT_ENV_ARG_LIMIT=32 +# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set +# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set +# CONFIG_INIT_STACK_ALL_PATTERN is not set +# CONFIG_INIT_STACK_ALL_ZERO is not set +CONFIG_INIT_STACK_NONE=y +CONFIG_INOTIFY_USER=y +# CONFIG_INPUT is not set +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_APANEL is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_ATLAS_BTNS is not set +# CONFIG_INPUT_ATMEL_CAPTOUCH is not set +# CONFIG_INPUT_AXP20X_PEK is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_DA7280_HAPTICS is not set +# CONFIG_INPUT_DRV260X_HAPTICS is not set +# CONFIG_INPUT_DRV2665_HAPTICS is not set +# CONFIG_INPUT_DRV2667_HAPTICS is not set +# CONFIG_INPUT_E3X0_BUTTON is not set +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_GPIO_BEEPER is not set +# CONFIG_INPUT_GPIO_DECODER is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_GPIO_VIBRA is not set +# CONFIG_INPUT_IBM_PANEL is not set +# CONFIG_INPUT_IDEAPAD_SLIDEBAR is not set +# CONFIG_INPUT_IMS_PCU is not set +# CONFIG_INPUT_IQS269A is not set +# CONFIG_INPUT_IQS626A is not set +# CONFIG_INPUT_IQS7222 is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_LEDS is not set +# CONFIG_INPUT_MATRIXKMAP is not set +# CONFIG_INPUT_MAX8997_HAPTIC is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_PALMAS_PWRBUTTON is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_PCSPKR is not set +# CONFIG_INPUT_PM8941_PWRKEY is not set +# CONFIG_INPUT_PM8XXX_VIBRATOR is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_PWM_BEEPER is not set +# CONFIG_INPUT_PWM_VIBRA is not set +# CONFIG_INPUT_REGULATOR_HAPTIC is not set +# CONFIG_INPUT_SOC_BUTTON_ARRAY is not set +# CONFIG_INPUT_SPARSEKMAP is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_TPS65218_PWRBUTTON is not set +# CONFIG_INPUT_TWL4030_PWRBUTTON is not set +# CONFIG_INPUT_TWL4030_VIBRA is not set +# CONFIG_INPUT_TWL6040_VIBRA is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_WISTRON_BTNS is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INSPUR_PLATFORM_PROFILE is not set +# CONFIG_INT340X_THERMAL is not set +# CONFIG_INTEGRITY is not set +# CONFIG_INTEGRITY_AUDIT is not set +# CONFIG_INTEGRITY_SIGNATURE is not set +# CONFIG_INTEL_ATOMISP2_LED is not set +# CONFIG_INTEL_ATOMISP2_PM is not set +# CONFIG_INTEL_HID_EVENT is not set +# CONFIG_INTEL_IDLE is not set +# CONFIG_INTEL_IDMA64 is not set +# CONFIG_INTEL_INT0002_VGPIO is not set +# CONFIG_INTEL_IOATDMA is not set +# CONFIG_INTEL_ISH_HID is not set +# CONFIG_INTEL_MEI is not set +# CONFIG_INTEL_MEI_GSC_PROXY is not set +# CONFIG_INTEL_MEI_HDCP is not set +# CONFIG_INTEL_MEI_ME is not set +# CONFIG_INTEL_MEI_PXP is not set +# CONFIG_INTEL_MEI_TXE is not set +# CONFIG_INTEL_OAKTRAIL is not set +# CONFIG_INTEL_PMC_CORE is not set +# CONFIG_INTEL_PUNIT_IPC is not set +# CONFIG_INTEL_RST is not set +# CONFIG_INTEL_SKL_INT3472 is not set +# CONFIG_INTEL_SMARTCONNECT is not set +# CONFIG_INTEL_SOC_PMIC is not set +# CONFIG_INTEL_SOC_PMIC_CHTDC_TI is not set +# CONFIG_INTEL_SOC_PMIC_CHTWC is not set +# CONFIG_INTEL_TH is not set +# CONFIG_INTEL_VBTN is not set +# CONFIG_INTEL_WMI_SBL_FW_UPDATE is not set +# CONFIG_INTEL_WMI_THUNDERBOLT is not set +# CONFIG_INTEL_XWAY_PHY is not set +# CONFIG_INTERCONNECT is not set +# CONFIG_INTERVAL_TREE_TEST is not set +# CONFIG_INV_ICM42600_I2C is not set +# CONFIG_INV_ICM42600_SPI is not set +# CONFIG_INV_MPU6050_I2C is not set +# CONFIG_INV_MPU6050_IIO is not set +# CONFIG_INV_MPU6050_SPI is not set +# CONFIG_IOMMU_SUPPORT is not set +# CONFIG_IONIC is not set +# CONFIG_IOSCHED_BFQ is not set +# CONFIG_IOSM is not set +CONFIG_IO_STRICT_DEVMEM=y +# CONFIG_IO_URING is not set +CONFIG_IO_WQ=y +# CONFIG_IP17XX_PHY is not set +# CONFIG_IP5XXX_POWER is not set +# CONFIG_IP6_NF_FILTER is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_IP6_NF_MANGLE is not set +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_RPFILTER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_MATCH_SRH is not set +# CONFIG_IP6_NF_NAT is not set +# CONFIG_IP6_NF_RAW is not set +# CONFIG_IP6_NF_SECURITY is not set +# CONFIG_IP6_NF_TARGET_HL is not set +# CONFIG_IP6_NF_TARGET_MASQUERADE is not set +# CONFIG_IP6_NF_TARGET_REJECT is not set +# CONFIG_IP6_NF_TARGET_SYNPROXY is not set +# CONFIG_IPACK_BUS is not set +# CONFIG_IPC_NS is not set +# CONFIG_IPMB_DEVICE_INTERFACE is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_IPU_BRIDGE is not set +# CONFIG_IPV6 is not set +# CONFIG_IPV6_FOU is not set +# CONFIG_IPV6_FOU_TUNNEL is not set +# CONFIG_IPV6_ILA is not set +# CONFIG_IPV6_IOAM6_LWTUNNEL is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_IPV6_MROUTE_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_ROUTE_INFO is not set +# CONFIG_IPV6_RPL_LWTUNNEL is not set +# CONFIG_IPV6_SEG6_HMAC is not set +# CONFIG_IPV6_SEG6_LWTUNNEL is not set +# CONFIG_IPV6_SIT is not set +# CONFIG_IPV6_SIT_6RD is not set +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_VTI is not set +# CONFIG_IPVLAN is not set +# CONFIG_IPVTAP is not set +# CONFIG_IPW2100 is not set +# CONFIG_IPW2100_DEBUG is not set +CONFIG_IPW2100_MONITOR=y +# CONFIG_IPW2200 is not set +# CONFIG_IPW2200_DEBUG is not set +CONFIG_IPW2200_MONITOR=y +# CONFIG_IPW2200_PROMISCUOUS is not set +# CONFIG_IPW2200_QOS is not set +# CONFIG_IPW2200_RADIOTAP is not set +# CONFIG_IPWIRELESS is not set +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_DCCP is not set +# CONFIG_IP_FIB_TRIE_STATS is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_NF_ARPFILTER is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_NF_ARP_MANGLE is not set +# CONFIG_IP_NF_FILTER is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_MATCH_AH is not set +# CONFIG_IP_NF_MATCH_ECN is not set +# CONFIG_IP_NF_MATCH_RPFILTER is not set +# CONFIG_IP_NF_MATCH_TTL is not set +# CONFIG_IP_NF_RAW is not set +# CONFIG_IP_NF_SECURITY is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_MASQUERADE is not set +# CONFIG_IP_NF_TARGET_NETMAP is not set +# CONFIG_IP_NF_TARGET_REDIRECT is not set +# CONFIG_IP_NF_TARGET_REJECT is not set +# CONFIG_IP_NF_TARGET_SYNPROXY is not set +# CONFIG_IP_NF_TARGET_TTL is not set +# CONFIG_IP_PIMSM_V1 is not set +# CONFIG_IP_PIMSM_V2 is not set +# CONFIG_IP_PNP is not set +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_SCTP is not set +# CONFIG_IP_SET is not set +# CONFIG_IP_SET_HASH_IPMAC is not set +# CONFIG_IP_VS is not set +# CONFIG_IP_VS_MH is not set +CONFIG_IP_VS_MH_TAB_INDEX=10 +# CONFIG_IP_VS_TWOS is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_IRQ_ALL_CPUS is not set +# CONFIG_IRQ_POLL is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_IRSD200 is not set +# CONFIG_IR_GPIO_CIR is not set +# CONFIG_IR_HIX5HD2 is not set +# CONFIG_IR_IGORPLUGUSB is not set +# CONFIG_IR_IGUANA is not set +# CONFIG_IR_IMG is not set +# CONFIG_IR_IMON is not set +# CONFIG_IR_IMON_RAW is not set +# CONFIG_IR_JVC_DECODER is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_NEC_DECODER is not set +# CONFIG_IR_RC5_DECODER is not set +# CONFIG_IR_RC6_DECODER is not set +# CONFIG_IR_REDRAT3 is not set +# CONFIG_IR_SERIAL is not set +# CONFIG_IR_SONY_DECODER is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_IR_TOY is not set +# CONFIG_IR_TTUSBIR is not set +# CONFIG_ISA_BUS is not set +# CONFIG_ISA_BUS_API is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_ISCSI_TCP is not set +CONFIG_ISDN=y +# CONFIG_ISDN_CAPI is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_ISL29125 is not set +# CONFIG_ISL29501 is not set +# CONFIG_ISL76682 is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_ISS4xx is not set +# CONFIG_ITG3200 is not set +# CONFIG_IWL3945 is not set +# CONFIG_IWLWIFI is not set +# CONFIG_IXGBE is not set +# CONFIG_IXGBEVF is not set +# CONFIG_JAILHOUSE_GUEST is not set +# CONFIG_JBD2_DEBUG is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_POSIX_ACL is not set +# CONFIG_JFFS2_FS_SECURITY is not set +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_FS_WRITEBUFFER=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_LZMA=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_POSIX_ACL is not set +# CONFIG_JFS_SECURITY is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_JME is not set +CONFIG_JOLIET=y +# CONFIG_JSA1212 is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_JZ4740_WDT is not set +# CONFIG_KALLSYMS is not set +# CONFIG_KALLSYMS_ABSOLUTE_PERCPU is not set +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_SELFTEST is not set +# CONFIG_KALLSYMS_UNCOMPRESSED is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_KASAN is not set +# CONFIG_KASAN_MODULE_TEST is not set +CONFIG_KASAN_STACK=y +# CONFIG_KCMP is not set +# CONFIG_KCOV is not set +CONFIG_KCOV_IRQ_AREA_SIZE=0x40000 +# CONFIG_KCSAN is not set +# CONFIG_KEBA_CP500 is not set +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZ4 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_KERNEL_MODE_NEON=y +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_ZSTD is not set +CONFIG_KERNFS=y +# CONFIG_KEXEC is not set +# CONFIG_KEXEC_FILE is not set +# CONFIG_KEXEC_SIG is not set +# CONFIG_KEYBOARD_ADC is not set +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_APPLESPI is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_BCM is not set +# CONFIG_KEYBOARD_CAP11XX is not set +# CONFIG_KEYBOARD_CYPRESS_SF is not set +# CONFIG_KEYBOARD_DLINK_DIR685 is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_MT6779 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_PINEPHONE is not set +# CONFIG_KEYBOARD_PXA27x is not set +# CONFIG_KEYBOARD_QT1050 is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_SH_KEYSC is not set +# CONFIG_KEYBOARD_SNVS_PWRKEY is not set +# CONFIG_KEYBOARD_STMPE is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_TEGRA is not set +# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set +# CONFIG_KEYBOARD_TWL4030 is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYS is not set +# CONFIG_KEYS_REQUEST_CACHE is not set +# CONFIG_KEY_DH_OPERATIONS is not set +# CONFIG_KFENCE is not set +# CONFIG_KGDB is not set +# CONFIG_KMX61 is not set +# CONFIG_KPROBES is not set +# CONFIG_KPROBES_SANITY_TEST is not set +# CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set +# CONFIG_KPROBE_EVENT_GEN_TEST is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_KSM is not set +# CONFIG_KSZ884X_PCI is not set +# CONFIG_KUNIT is not set +CONFIG_KUSER_HELPERS=y +# CONFIG_KVM_AMD is not set +# CONFIG_KVM_AMD_SEV is not set +# CONFIG_KVM_GUEST is not set +# CONFIG_KVM_HYPERV is not set +# CONFIG_KVM_INTEL is not set +# CONFIG_KVM_INTEL_PROVE_VE is not set +CONFIG_KVM_MAX_NR_VCPUS=1024 +# CONFIG_KVM_PROVE_MMU is not set +# CONFIG_KVM_SW_PROTECTED_VM is not set +# CONFIG_KVM_WERROR is not set +# CONFIG_KVM_XEN is not set +# CONFIG_KXCJK1013 is not set +# CONFIG_KXSD9 is not set +# CONFIG_L2TP is not set +# CONFIG_L2TP_ETH is not set +# CONFIG_L2TP_IP is not set +# CONFIG_L2TP_V3 is not set +# CONFIG_LAN743X is not set +# CONFIG_LAN865X is not set +# CONFIG_LAN966X_OIC is not set +# CONFIG_LAN966X_SWITCH is not set +# CONFIG_LANTIQ is not set +# CONFIG_LAPB is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +# CONFIG_LCD_AMS369FG06 is not set +# CONFIG_LCD_CLASS_DEVICE is not set +# CONFIG_LCD_HX8357 is not set +# CONFIG_LCD_ILI922X is not set +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_L4F00242T03 is not set +# CONFIG_LCD_LMS283GF05 is not set +# CONFIG_LCD_LMS501KF03 is not set +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_OTM3225A is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +CONFIG_LDISC_AUTOLOAD=y +# CONFIG_LDM_PARTITION is not set +CONFIG_LD_DEAD_CODE_DATA_ELIMINATION=y +# CONFIG_LD_HEAD_STUB_CATCH is not set +# CONFIG_LEDS_AN30259A is not set +# CONFIG_LEDS_APU is not set +# CONFIG_LEDS_AW200XX is not set +# CONFIG_LEDS_AW2013 is not set +# CONFIG_LEDS_BCM6328 is not set +# CONFIG_LEDS_BCM6358 is not set +# CONFIG_LEDS_BD2606MVV is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_BLINKM is not set +CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y +CONFIG_LEDS_CLASS=y +# CONFIG_LEDS_CLASS_FLASH is not set +CONFIG_LEDS_CLASS_MULTICOLOR=y +# CONFIG_LEDS_CR0014114 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_EL15203000 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_GROUP_MULTICOLOR is not set +# CONFIG_LEDS_INTEL_SS4200 is not set +# CONFIG_LEDS_IS31FL319X is not set +# CONFIG_LEDS_IS31FL32XX is not set +# CONFIG_LEDS_KTD202X is not set +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_LM3532 is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_LM3642 is not set +# CONFIG_LEDS_LM3692X is not set +# CONFIG_LEDS_LM3697 is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP3952 is not set +# CONFIG_LEDS_LP50XX is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_LP5562 is not set +# CONFIG_LEDS_LP5569 is not set +# CONFIG_LEDS_LP55XX_COMMON is not set +# CONFIG_LEDS_LP8501 is not set +# CONFIG_LEDS_LP8860 is not set +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_MLXCPLD is not set +# CONFIG_LEDS_MLXREG is not set +# CONFIG_LEDS_NCP5623 is not set +# CONFIG_LEDS_NIC78BX is not set +# CONFIG_LEDS_NS2 is not set +# CONFIG_LEDS_OT200 is not set +# CONFIG_LEDS_PCA9532 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA963X is not set +# CONFIG_LEDS_PCA995X is not set +# CONFIG_LEDS_PWM is not set +# CONFIG_LEDS_PWM_MULTICOLOR is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_SPI_BYTE is not set +# CONFIG_LEDS_ST1202 is not set +# CONFIG_LEDS_SUN50I_A100 is not set +# CONFIG_LEDS_SYSCON is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_TI_LMU_COMMON is not set +# CONFIG_LEDS_TLC591XX is not set +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_ACTIVITY is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_CAMERA is not set +# CONFIG_LEDS_TRIGGER_CPU is not set +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +# CONFIG_LEDS_TRIGGER_DISK is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_INPUT_EVENTS is not set +# CONFIG_LEDS_TRIGGER_MTD is not set +CONFIG_LEDS_TRIGGER_NETDEV=y +# CONFIG_LEDS_TRIGGER_ONESHOT is not set +# CONFIG_LEDS_TRIGGER_PANIC is not set +# CONFIG_LEDS_TRIGGER_PATTERN is not set +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_TRANSIENT is not set +# CONFIG_LEDS_TRIGGER_TTY is not set +# CONFIG_LEDS_TURRIS_OMNIA is not set +# CONFIG_LEDS_USER is not set +# CONFIG_LED_TRIGGER_PHY is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_LEGACY_TIOCSTI is not set +# CONFIG_LENOVO_WMI_CAMERA is not set +# CONFIG_LENOVO_YMC is not set +# CONFIG_LG_LAPTOP is not set +# CONFIG_LIB80211 is not set +# CONFIG_LIB80211_CRYPT_CCMP is not set +# CONFIG_LIB80211_CRYPT_TKIP is not set +# CONFIG_LIB80211_CRYPT_WEP is not set +# CONFIG_LIB80211_DEBUG is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_LIBERTAS is not set +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_LIBERTAS_USB is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_LIBIPW_DEBUG is not set +# CONFIG_LIBNVDIMM is not set +# CONFIG_LIDAR_LITE_V2 is not set +CONFIG_LINEAR_RANGES=y +# CONFIG_LIQUIDIO is not set +# CONFIG_LIQUIDIO_VF is not set +# CONFIG_LIRC is not set +CONFIG_LIST_HARDENED=y +# CONFIG_LITEX_LITEETH is not set +# CONFIG_LITEX_SOC_CONTROLLER is not set +# CONFIG_LIVEPATCH is not set +# CONFIG_LKDTM is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LMK04832 is not set +# CONFIG_LMP91000 is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_LOCKD is not set +CONFIG_LOCKDEP_BITS=15 +CONFIG_LOCKDEP_CHAINS_BITS=16 +CONFIG_LOCKDEP_CIRCULAR_QUEUE_BITS=12 +CONFIG_LOCKDEP_STACK_TRACE_BITS=19 +CONFIG_LOCKDEP_STACK_TRACE_HASH_BITS=14 +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_LOCKD_V4=y +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_LOCK_EVENT_COUNTS is not set +CONFIG_LOCK_MM_AND_FIND_VMA=y +# CONFIG_LOCK_STAT is not set +# CONFIG_LOCK_TORTURE_TEST is not set +# CONFIG_LOGIG940_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIWHEELS_FF is not set +# CONFIG_LOGO is not set +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 +# CONFIG_LOONGSON_MC146818 is not set +# CONFIG_LPC_ICH is not set +# CONFIG_LPC_SCH is not set +# CONFIG_LP_CONSOLE is not set +CONFIG_LRU_GEN=y +CONFIG_LRU_GEN_ENABLED=y +# CONFIG_LRU_GEN_STATS is not set +# CONFIG_LSI_ET1011C_PHY is not set +CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity" +CONFIG_LSM_MMAP_MIN_ADDR=65536 +# CONFIG_LTC1660 is not set +# CONFIG_LTC2309 is not set +# CONFIG_LTC2471 is not set +# CONFIG_LTC2485 is not set +# CONFIG_LTC2496 is not set +# CONFIG_LTC2497 is not set +# CONFIG_LTC2632 is not set +# CONFIG_LTC2664 is not set +# CONFIG_LTC2688 is not set +# CONFIG_LTC2983 is not set +# CONFIG_LTE_GDM724X is not set +CONFIG_LTO_NONE=y +# CONFIG_LTR390 is not set +# CONFIG_LTR501 is not set +# CONFIG_LTRF216A is not set +# CONFIG_LV0104CS is not set +# CONFIG_LWQ_TEST is not set +# CONFIG_LWTUNNEL is not set +# CONFIG_LXT_PHY is not set +# CONFIG_LZ4HC_COMPRESS is not set +# CONFIG_LZ4_COMPRESS is not set +# CONFIG_LZ4_DECOMPRESS is not set +CONFIG_LZMA_COMPRESS=y +CONFIG_LZMA_DECOMPRESS=y +# CONFIG_LZO_COMPRESS is not set +# CONFIG_LZO_DECOMPRESS is not set +# CONFIG_M62332 is not set +# CONFIG_MAC80211 is not set +# CONFIG_MAC80211_MESSAGE_TRACING is not set +CONFIG_MAC80211_STA_HASH_MAX_SIZE=0 +# CONFIG_MACB is not set +# CONFIG_MACH_ASM9260 is not set +# CONFIG_MACH_DECSTATION is not set +# CONFIG_MACH_INGENIC is not set +# CONFIG_MACH_INGENIC_SOC is not set +# CONFIG_MACH_JAZZ is not set +# CONFIG_MACH_JZ4740 is not set +# CONFIG_MACH_LOONGSON2EF is not set +# CONFIG_MACH_LOONGSON32 is not set +# CONFIG_MACH_LOONGSON64 is not set +# CONFIG_MACH_NINTENDO64 is not set +# CONFIG_MACH_PIC32 is not set +# CONFIG_MACH_REALTEK_RTL is not set +# CONFIG_MACH_TX49XX is not set +# CONFIG_MACINTOSH_DRIVERS is not set +# CONFIG_MACSEC is not set +# CONFIG_MACVLAN is not set +# CONFIG_MACVTAP is not set +# CONFIG_MAC_EMUMOUSEBTN is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_MAG3110 is not set +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 +# CONFIG_MAGIC_SYSRQ_SERIAL is not set +CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE="" +# CONFIG_MAILBOX is not set +# CONFIG_MANAGER_SBS is not set +# CONFIG_MANGLE_BOOTARGS is not set +# CONFIG_MARVELL_10G_PHY is not set +# CONFIG_MARVELL_88Q2XXX_PHY is not set +# CONFIG_MARVELL_88X2222_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_MAX1027 is not set +# CONFIG_MAX11100 is not set +# CONFIG_MAX1118 is not set +# CONFIG_MAX11205 is not set +# CONFIG_MAX11410 is not set +# CONFIG_MAX1241 is not set +# CONFIG_MAX1363 is not set +# CONFIG_MAX30100 is not set +# CONFIG_MAX30102 is not set +# CONFIG_MAX30208 is not set +# CONFIG_MAX31827 is not set +# CONFIG_MAX31856 is not set +# CONFIG_MAX31865 is not set +# CONFIG_MAX34408 is not set +# CONFIG_MAX44000 is not set +# CONFIG_MAX44009 is not set +# CONFIG_MAX517 is not set +# CONFIG_MAX5432 is not set +# CONFIG_MAX5481 is not set +# CONFIG_MAX5487 is not set +# CONFIG_MAX5522 is not set +# CONFIG_MAX5821 is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_MAX9611 is not set +# CONFIG_MAXIM_THERMOCOUPLE is not set +# CONFIG_MAXLINEAR_GPHY is not set +CONFIG_MAX_SKB_FRAGS=17 +# CONFIG_MB1232 is not set +# CONFIG_MC3230 is not set +# CONFIG_MCB is not set +# CONFIG_MCP320X is not set +# CONFIG_MCP3422 is not set +# CONFIG_MCP3564 is not set +# CONFIG_MCP3911 is not set +# CONFIG_MCP4018 is not set +# CONFIG_MCP41010 is not set +# CONFIG_MCP4131 is not set +# CONFIG_MCP4531 is not set +# CONFIG_MCP4725 is not set +# CONFIG_MCP4728 is not set +# CONFIG_MCP4821 is not set +# CONFIG_MCP4922 is not set +# CONFIG_MCP9600 is not set +# CONFIG_MCPM is not set +# CONFIG_MCTP is not set +# CONFIG_MD is not set +# CONFIG_MDIO_BCM_UNIMAC is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set +# CONFIG_MDIO_BUS_MUX_MULTIPLEXER is not set +# CONFIG_MDIO_DEVICE is not set +# CONFIG_MDIO_DEVRES is not set +# CONFIG_MDIO_HISI_FEMAC is not set +# CONFIG_MDIO_IPQ4019 is not set +# CONFIG_MDIO_IPQ8064 is not set +# CONFIG_MDIO_MSCC_MIIM is not set +# CONFIG_MDIO_MVUSB is not set +# CONFIG_MDIO_OCTEON is not set +# CONFIG_MDIO_THUNDER is not set +# CONFIG_MDM_GCC_9607 is not set +# CONFIG_MD_BITMAP_FILE is not set +# CONFIG_MEDIATEK_GE_PHY is not set +# CONFIG_MEDIATEK_MT6577_AUXADC is not set +# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set +# CONFIG_MEDIA_ATTACH is not set +# CONFIG_MEDIA_CAMERA_SUPPORT is not set +# CONFIG_MEDIA_CEC_SUPPORT is not set +# CONFIG_MEDIA_CONTROLLER is not set +# CONFIG_MEDIA_CONTROLLER_DVB is not set +# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set +# CONFIG_MEDIA_PCI_SUPPORT is not set +# CONFIG_MEDIA_PLATFORM_DRIVERS is not set +# CONFIG_MEDIA_PLATFORM_SUPPORT is not set +# CONFIG_MEDIA_RADIO_SUPPORT is not set +# CONFIG_MEDIA_SDR_SUPPORT is not set +# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set +# CONFIG_MEDIA_SUPPORT is not set +# CONFIG_MEDIA_SUPPORT_FILTER is not set +# CONFIG_MEDIA_TEST_SUPPORT is not set +# CONFIG_MEDIA_TUNER_E4000 is not set +# CONFIG_MEDIA_TUNER_FC0011 is not set +# CONFIG_MEDIA_TUNER_FC0012 is not set +# CONFIG_MEDIA_TUNER_FC0013 is not set +# CONFIG_MEDIA_TUNER_FC2580 is not set +# CONFIG_MEDIA_TUNER_IT913X is not set +# CONFIG_MEDIA_TUNER_M88RS6000T is not set +# CONFIG_MEDIA_TUNER_MAX2165 is not set +# CONFIG_MEDIA_TUNER_MC44S803 is not set +# CONFIG_MEDIA_TUNER_MSI001 is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2063 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MXL301RF is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +# CONFIG_MEDIA_TUNER_QM1D1B0004 is not set +# CONFIG_MEDIA_TUNER_QM1D1C0042 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_R820T is not set +# CONFIG_MEDIA_TUNER_SI2157 is not set +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA18212 is not set +# CONFIG_MEDIA_TUNER_TDA18218 is not set +# CONFIG_MEDIA_TUNER_TDA18250 is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_TUA9001 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC4000 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +# CONFIG_MEDIA_USB_SUPPORT is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_SAS is not set +# CONFIG_MELLANOX_PLATFORM is not set +CONFIG_MEMBARRIER=y +CONFIG_MEMFD_CREATE=y +# CONFIG_MEMORY is not set +# CONFIG_MEMORY_FAILURE is not set +# CONFIG_MEMORY_HOTPLUG is not set +# CONFIG_MEMSTICK is not set +# CONFIG_MEMTEST is not set +# CONFIG_MEM_ALLOC_PROFILING is not set +# CONFIG_MEN_A21_WDT is not set +# CONFIG_MESON_SM is not set +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_88PM886_PMIC is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_AC100 is not set +# CONFIG_MFD_ACT8945A is not set +# CONFIG_MFD_ADP5585 is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_AS3711 is not set +# CONFIG_MFD_AS3722 is not set +# CONFIG_MFD_ATC260X_I2C is not set +# CONFIG_MFD_ATMEL_FLEXCOM is not set +# CONFIG_MFD_ATMEL_HLCDC is not set +# CONFIG_MFD_AXP20X is not set +# CONFIG_MFD_AXP20X_I2C is not set +# CONFIG_MFD_BCM590XX is not set +# CONFIG_MFD_BD9571MWV is not set +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_CPCAP is not set +# CONFIG_MFD_CS40L50_I2C is not set +# CONFIG_MFD_CS40L50_SPI is not set +# CONFIG_MFD_CS42L43_I2C is not set +# CONFIG_MFD_CS5535 is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_DA9062 is not set +# CONFIG_MFD_DA9063 is not set +# CONFIG_MFD_DA9150 is not set +# CONFIG_MFD_DLN2 is not set +# CONFIG_MFD_EXYNOS_LPASS is not set +# CONFIG_MFD_GATEWORKS_GSC is not set +# CONFIG_MFD_HI6421_PMIC is not set +# CONFIG_MFD_INTEL_M10_BMC_SPI is not set +# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set +# CONFIG_MFD_IQS62X is not set +# CONFIG_MFD_JANZ_CMODIO is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_LOCHNAGAR is not set +# CONFIG_MFD_LP3943 is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_MADERA is not set +# CONFIG_MFD_MAX14577 is not set +# CONFIG_MFD_MAX5970 is not set +# CONFIG_MFD_MAX77541 is not set +# CONFIG_MFD_MAX77620 is not set +# CONFIG_MFD_MAX77650 is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX77714 is not set +# CONFIG_MFD_MAX77843 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MENF21BMC is not set +# CONFIG_MFD_MP2629 is not set +# CONFIG_MFD_MT6360 is not set +# CONFIG_MFD_MT6370 is not set +# CONFIG_MFD_MT6397 is not set +# CONFIG_MFD_NTXEC is not set +# CONFIG_MFD_OCELOT is not set +# CONFIG_MFD_OMAP_USB_HOST is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_PM8XXX is not set +# CONFIG_MFD_QCOM_PM8008 is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_RDC321X is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_RK8XX_I2C is not set +# CONFIG_MFD_RK8XX_SPI is not set +# CONFIG_MFD_RN5T618 is not set +# CONFIG_MFD_ROHM_BD71828 is not set +# CONFIG_MFD_ROHM_BD718XX is not set +# CONFIG_MFD_ROHM_BD957XMUF is not set +# CONFIG_MFD_ROHM_BD96801 is not set +# CONFIG_MFD_RSMU_I2C is not set +# CONFIG_MFD_RSMU_SPI is not set +# CONFIG_MFD_RT4831 is not set +# CONFIG_MFD_RT5033 is not set +# CONFIG_MFD_RT5120 is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SKY81452 is not set +# CONFIG_MFD_SL28CPLD is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SMPRO is not set +# CONFIG_MFD_SPACEMIT_P1 is not set +# CONFIG_MFD_STMFX is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_STPMIC1 is not set +# CONFIG_MFD_SY7636A is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TIMBERDALE is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_TI_LMU is not set +# CONFIG_MFD_TI_LP873X is not set +# CONFIG_MFD_TI_LP87565 is not set +# CONFIG_MFD_TN48M_CPLD is not set +# CONFIG_MFD_TPS65086 is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS65218 is not set +# CONFIG_MFD_TPS65219 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_TPS6594_I2C is not set +# CONFIG_MFD_TPS6594_SPI is not set +# CONFIG_MFD_TQMX86 is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_VX855 is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MHI_BUS is not set +# CONFIG_MHI_BUS_DEBUG is not set +# CONFIG_MHI_BUS_EP is not set +# CONFIG_MHI_BUS_PCI_GENERIC is not set +# CONFIG_MHI_NET is not set +# CONFIG_MHI_WWAN_CTRL is not set +# CONFIG_MHI_WWAN_MBIM is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_MICROCHIP_PHY is not set +# CONFIG_MICROCHIP_PIT64B is not set +# CONFIG_MICROCHIP_T1S_PHY is not set +# CONFIG_MICROCHIP_T1_PHY is not set +# CONFIG_MICROSEMI_PHY is not set +# CONFIG_MIGRATION is not set +CONFIG_MII=y +# CONFIG_MIKROTIK is not set +# CONFIG_MIKROTIK_RB532 is not set +# CONFIG_MINIX_FS is not set +# CONFIG_MINIX_FS_NATIVE_ENDIAN is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_MIPS32_N32 is not set +# CONFIG_MIPS32_O32 is not set +# CONFIG_MIPS_ALCHEMY is not set +# CONFIG_MIPS_CDMM is not set +# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set +# CONFIG_MIPS_CMDLINE_FROM_DTB is not set +# CONFIG_MIPS_COBALT is not set +# CONFIG_MIPS_CPS is not set +# CONFIG_MIPS_ELF_APPENDED_DTB is not set +# CONFIG_MIPS_FP_SUPPORT is not set +# CONFIG_MIPS_GENERIC is not set +# CONFIG_MIPS_GENERIC_KERNEL is not set +# CONFIG_MIPS_MALTA is not set +# CONFIG_MIPS_O32_FP64_SUPPORT is not set +# CONFIG_MIPS_PLATFORM_DEVICES is not set +# CONFIG_MIPS_RAW_APPENDED_DTB is not set +# CONFIG_MIPS_VA_BITS_48 is not set +# CONFIG_MIPS_VPE_LOADER is not set +# CONFIG_MISC_ALCOR_PCI is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_MISC_RTSX_PCI is not set +# CONFIG_MISC_RTSX_USB is not set +# CONFIG_MISDN is not set +# CONFIG_MISDN_AVMFRITZ is not set +# CONFIG_MISDN_HFCPCI is not set +# CONFIG_MISDN_HFCUSB is not set +# CONFIG_MISDN_INFINEON is not set +# CONFIG_MISDN_NETJET is not set +# CONFIG_MISDN_SPEEDFAX is not set +# CONFIG_MISDN_W6692 is not set +CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY=y +# CONFIG_MKISS is not set +# CONFIG_MLX4_CORE is not set +# CONFIG_MLX4_EN is not set +# CONFIG_MLX5_CORE is not set +# CONFIG_MLX5_DPLL is not set +# CONFIG_MLX5_MACSEC is not set +# CONFIG_MLX5_SF is not set +# CONFIG_MLX5_VFIO_PCI is not set +# CONFIG_MLX90614 is not set +# CONFIG_MLX90632 is not set +# CONFIG_MLX90635 is not set +# CONFIG_MLXFW is not set +# CONFIG_MLXSW_CORE is not set +# CONFIG_MLX_PLATFORM is not set +# CONFIG_MMA7455_I2C is not set +# CONFIG_MMA7455_SPI is not set +# CONFIG_MMA7660 is not set +# CONFIG_MMA8452 is not set +# CONFIG_MMA9551 is not set +# CONFIG_MMA9553 is not set +# CONFIG_MMC is not set +# CONFIG_MMC35240 is not set +# CONFIG_MMC_ARMMMCI is not set +# CONFIG_MMC_AU1X is not set +# CONFIG_MMC_BLOCK is not set +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_CAVIUM_THUNDERX is not set +# CONFIG_MMC_CB710 is not set +# CONFIG_MMC_CQHCI is not set +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_HSQ is not set +# CONFIG_MMC_JZ4740 is not set +# CONFIG_MMC_MTK is not set +# CONFIG_MMC_MVSDIO is not set +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_ACPI is not set +# CONFIG_MMC_SDHCI_AM654 is not set +# CONFIG_MMC_SDHCI_BCM_KONA is not set +# CONFIG_MMC_SDHCI_BRCMSTB is not set +# CONFIG_MMC_SDHCI_CADENCE is not set +# CONFIG_MMC_SDHCI_F_SDH30 is not set +# CONFIG_MMC_SDHCI_IPROC is not set +# CONFIG_MMC_SDHCI_MILBEAUT is not set +# CONFIG_MMC_SDHCI_MSM is not set +# CONFIG_MMC_SDHCI_OF_ARASAN is not set +# CONFIG_MMC_SDHCI_OF_ASPEED is not set +# CONFIG_MMC_SDHCI_OF_AT91 is not set +# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set +# CONFIG_MMC_SDHCI_OF_ESDHC is not set +# CONFIG_MMC_SDHCI_OF_HLWD is not set +# CONFIG_MMC_SDHCI_OMAP is not set +# CONFIG_MMC_SDHCI_PCI is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_S3C is not set +# CONFIG_MMC_SDHCI_XENON is not set +# CONFIG_MMC_SDRICOH_CS is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_STM32_SDMMC is not set +# CONFIG_MMC_TEST is not set +# CONFIG_MMC_TIFM_SD is not set +# CONFIG_MMC_TOSHIBA_PCI is not set +# CONFIG_MMC_USDHI6ROL0 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MMC_VIA_SDMMC is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMIOTRACE is not set +CONFIG_MMU=y +CONFIG_MMU_GATHER_RCU_TABLE_FREE=y +CONFIG_MMU_GATHER_TABLE_FREE=y +CONFIG_MODPROBE_PATH="/sbin/modprobe" +CONFIG_MODULES=y +# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set +# CONFIG_MODULE_COMPRESS is not set +# CONFIG_MODULE_COMPRESS_GZIP is not set +# CONFIG_MODULE_COMPRESS_XZ is not set +# CONFIG_MODULE_COMPRESS_ZSTD is not set +# CONFIG_MODULE_DEBUG is not set +# CONFIG_MODULE_FORCE_LOAD is not set +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODULE_SIG is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_MODULE_STRIPPED=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_UNLOAD_TAINT_TRACKING is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MOST is not set +# CONFIG_MOTORCOMM_PHY is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_CYAPA is not set +# CONFIG_MOUSE_ELAN_I2C is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +# CONFIG_MOUSE_PS2_FOCALTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_MOUSE_SYNAPTICS_USB is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOXTET is not set +# CONFIG_MPL115 is not set +# CONFIG_MPL115_I2C is not set +# CONFIG_MPL115_SPI is not set +# CONFIG_MPL3115 is not set +# CONFIG_MPLS is not set +# CONFIG_MPLS_IPTUNNEL is not set +# CONFIG_MPLS_ROUTING is not set +# CONFIG_MPRLS0025PA is not set +# CONFIG_MPTCP is not set +# CONFIG_MPU3050_I2C is not set +# CONFIG_MQ_IOSCHED_DEADLINE is not set +# CONFIG_MQ_IOSCHED_KYBER is not set +# CONFIG_MS5611 is not set +# CONFIG_MS5637 is not set +# CONFIG_MSA311 is not set +# CONFIG_MSCC_OCELOT_SWITCH is not set +# CONFIG_MSDOS_FS is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_MSE102X is not set +# CONFIG_MSI_BITMAP_SELFTEST is not set +# CONFIG_MSI_LAPTOP is not set +# CONFIG_MSI_WMI is not set +# CONFIG_MSI_WMI_PLATFORM is not set +# CONFIG_MSM_GCC_8953 is not set +# CONFIG_MSM_MMCC_8994 is not set +# CONFIG_MST_IRQ is not set +CONFIG_MTD=y +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_AFS_PARTS is not set +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_MTD_BLOCK2MTD is not set +CONFIG_MTD_CFI=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_CMDLINE_PARTS is not set +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_HYPERBUS is not set +# CONFIG_MTD_IMPA7 is not set +# CONFIG_MTD_JEDECPROBE is not set +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_LPDDR2_NVM is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +CONFIG_MTD_MAP_BANK_WIDTH_2=y +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MCHP23K256 is not set +# CONFIG_MTD_MCHP48L640 is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_MYLOADER_PARTS is not set +# CONFIG_MTD_NAND_AMS_DELTA is not set +# CONFIG_MTD_NAND_ARASAN is not set +# CONFIG_MTD_NAND_ATMEL is not set +# CONFIG_MTD_NAND_AU1550 is not set +# CONFIG_MTD_NAND_BRCMNAND is not set +# CONFIG_MTD_NAND_BRCMNAND_BCM63XX is not set +# CONFIG_MTD_NAND_BRCMNAND_BCMBCA is not set +# CONFIG_MTD_NAND_BRCMNAND_BRCMSTB is not set +# CONFIG_MTD_NAND_BRCMNAND_IPROC is not set +# CONFIG_MTD_NAND_CADENCE is not set +# CONFIG_MTD_NAND_CAFE is not set +# CONFIG_MTD_NAND_CS553X is not set +# CONFIG_MTD_NAND_DAVINCI is not set +# CONFIG_MTD_NAND_DENALI is not set +# CONFIG_MTD_NAND_DENALI_DT is not set +# CONFIG_MTD_NAND_DENALI_PCI is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_ECC is not set +# CONFIG_MTD_NAND_ECC_MXIC is not set +# CONFIG_MTD_NAND_ECC_SW_BCH is not set +# CONFIG_MTD_NAND_ECC_SW_HAMMING is not set +# CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC is not set +# CONFIG_MTD_NAND_FSL_ELBC is not set +# CONFIG_MTD_NAND_FSL_IFC is not set +# CONFIG_MTD_NAND_FSL_UPM is not set +# CONFIG_MTD_NAND_FSMC is not set +# CONFIG_MTD_NAND_GPIO is not set +# CONFIG_MTD_NAND_GPMI_NAND is not set +# CONFIG_MTD_NAND_HISI504 is not set +# CONFIG_MTD_NAND_INTEL_LGM is not set +# CONFIG_MTD_NAND_MPC5121_NFC is not set +# CONFIG_MTD_NAND_MTK is not set +# CONFIG_MTD_NAND_MTK_BMT is not set +# CONFIG_MTD_NAND_MXC is not set +# CONFIG_MTD_NAND_MXIC is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_NDFC is not set +# CONFIG_MTD_NAND_OMAP2 is not set +# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set +# CONFIG_MTD_NAND_ORION is not set +# CONFIG_MTD_NAND_PASEMI is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_NAND_RICOH is not set +# CONFIG_MTD_NAND_S3C2410 is not set +# CONFIG_MTD_NAND_SHARPSL is not set +# CONFIG_MTD_NAND_SH_FLCTL is not set +# CONFIG_MTD_NAND_SOCRATES is not set +# CONFIG_MTD_NAND_TXX9NDFMC is not set +CONFIG_MTD_OF_PARTS=y +# CONFIG_MTD_ONENAND is not set +# CONFIG_MTD_OOPS is not set +# CONFIG_MTD_OTP is not set +# CONFIG_MTD_PARSER_TRX is not set +# CONFIG_MTD_PARTITIONED_MASTER is not set +# CONFIG_MTD_PCI is not set +# CONFIG_MTD_PCMCIA is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PHYSMAP_COMPAT is not set +# CONFIG_MTD_PHYSMAP_GEMINI is not set +# CONFIG_MTD_PHYSMAP_GPIO_ADDR is not set +# CONFIG_MTD_PHYSMAP_IXP4XX is not set +CONFIG_MTD_PHYSMAP_OF=y +# CONFIG_MTD_PHYSMAP_VERSATILE is not set +# CONFIG_MTD_PLATRAM is not set +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_RAW_NAND is not set +CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1 +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_ROM is not set +CONFIG_MTD_ROOTFS_ROOT_DEV=y +# CONFIG_MTD_ROUTERBOOT_PARTS is not set +# CONFIG_MTD_SERCOMM_PARTS is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_SM_COMMON is not set +# CONFIG_MTD_SPI_NAND is not set +# CONFIG_MTD_SPI_NOR is not set +# CONFIG_MTD_SPI_NOR_SWP_DISABLE is not set +CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE=y +# CONFIG_MTD_SPI_NOR_SWP_KEEP is not set +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +# CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE is not set +CONFIG_MTD_SPLIT=y +# CONFIG_MTD_SPLIT_BCM63XX_FW is not set +# CONFIG_MTD_SPLIT_BCM_WFI_FW is not set +# CONFIG_MTD_SPLIT_BRNIMAGE_FW is not set +# CONFIG_MTD_SPLIT_ELF_FW is not set +# CONFIG_MTD_SPLIT_EVA_FW is not set +# CONFIG_MTD_SPLIT_FIRMWARE is not set +CONFIG_MTD_SPLIT_FIRMWARE_NAME="firmware" +# CONFIG_MTD_SPLIT_FIT_FW is not set +# CONFIG_MTD_SPLIT_H3C_VFS is not set +# CONFIG_MTD_SPLIT_JIMAGE_FW is not set +# CONFIG_MTD_SPLIT_LZMA_FW is not set +# CONFIG_MTD_SPLIT_MINOR_FW is not set +# CONFIG_MTD_SPLIT_MSTC_BOOT is not set +# CONFIG_MTD_SPLIT_OPENWRT_PROLOG is not set +# CONFIG_MTD_SPLIT_SEAMA_FW is not set +# CONFIG_MTD_SPLIT_SEIL_FW is not set +CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y +CONFIG_MTD_SPLIT_SUPPORT=y +# CONFIG_MTD_SPLIT_TPLINK_FW is not set +# CONFIG_MTD_SPLIT_TRX_FW is not set +# CONFIG_MTD_SPLIT_UIMAGE_FW is not set +# CONFIG_MTD_SPLIT_WRGG_FW is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SWAP is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_UBI is not set +# CONFIG_MTD_UBI_FASTMAP is not set +# CONFIG_MTD_UBI_GLUEBI is not set +# CONFIG_MTD_UBI_NVMEM is not set +# CONFIG_MTD_VIRT_CONCAT is not set +# CONFIG_MTK_DEVAPC is not set +# CONFIG_MTK_MMSYS is not set +# CONFIG_MTK_T7XX is not set +# CONFIG_MTK_THERMAL is not set +# CONFIG_MULTIPLEXER is not set +CONFIG_MULTIUSER=y +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +# CONFIG_MUX_ADG792A is not set +# CONFIG_MUX_ADGS1408 is not set +# CONFIG_MUX_GPIO is not set +# CONFIG_MUX_MMIO is not set +# CONFIG_MV643XX_ETH is not set +# CONFIG_MVMDIO is not set +# CONFIG_MVNETA_BM is not set +# CONFIG_MV_XOR_V2 is not set +# CONFIG_MWAVE is not set +# CONFIG_MWL8K is not set +# CONFIG_MXC4005 is not set +# CONFIG_MXC6255 is not set +# CONFIG_MXM_WMI is not set +# CONFIG_MYRI10GE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_NATSEMI is not set +# CONFIG_NAU7802 is not set +# CONFIG_NBPFAXI_DMA is not set +# CONFIG_NCN26000_PHY is not set +# CONFIG_NE2000 is not set +# CONFIG_NE2K_PCI is not set +CONFIG_NEED_TASKS_RCU=y +CONFIG_NET=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETCONSOLE_EXTENDED_LOG is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVSIM is not set +# CONFIG_NETFILTER is not set +# CONFIG_NETFILTER_ADVANCED is not set +# CONFIG_NETFILTER_EGRESS is not set +# CONFIG_NETFILTER_INGRESS is not set +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set +# CONFIG_NETFILTER_NETLINK_HOOK is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NETFILTER_NETLINK_OSF is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_NETFILTER_XTABLES_COMPAT is not set +# CONFIG_NETFILTER_XT_CONNMARK is not set +# CONFIG_NETFILTER_XT_MARK is not set +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_BPF is not set +# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set +# CONFIG_NETFILTER_XT_MATCH_CONNTRACK is not set +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ECN is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_HELPER is not set +# CONFIG_NETFILTER_XT_MATCH_HL is not set +# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set +# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set +# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_MAC is not set +# CONFIG_NETFILTER_XT_MATCH_MARK is not set +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_POLICY is not set +# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set +# CONFIG_NETFILTER_XT_MATCH_STATE is not set +# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set +# CONFIG_NETFILTER_XT_MATCH_STRING is not set +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +# CONFIG_NETFILTER_XT_MATCH_TIME is not set +# CONFIG_NETFILTER_XT_MATCH_U32 is not set +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set +# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_HMARK is not set +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +# CONFIG_NETFILTER_XT_TARGET_LED is not set +# CONFIG_NETFILTER_XT_TARGET_LOG is not set +# CONFIG_NETFILTER_XT_TARGET_MARK is not set +# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_REDIRECT is not set +# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +# CONFIG_NETFS_DEBUG is not set +# CONFIG_NETFS_STATS is not set +# CONFIG_NETKIT is not set +# CONFIG_NETLABEL is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NETROM is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETXEN_NIC is not set +# CONFIG_NET_9P is not set +# CONFIG_NET_9P_USBG is not set +# CONFIG_NET_9P_XEN is not set +# CONFIG_NET_ACT_BPF is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_ACT_CT is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_GATE is not set +# CONFIG_NET_ACT_IFE is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_MPLS is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_SAMPLE is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_SKBMOD is not set +# CONFIG_NET_ACT_TUNNEL_KEY is not set +# CONFIG_NET_ACT_VLAN is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_ACT is not set +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_BPF is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_FLOWER is not set +# CONFIG_NET_CLS_FW is not set +# CONFIG_NET_CLS_MATCHALL is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_U32 is not set +CONFIG_NET_CORE=y +# CONFIG_NET_DEVLINK is not set +# CONFIG_NET_DEV_REFCNT_TRACKER is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_NET_DSA is not set +# CONFIG_NET_DSA_AR9331 is not set +# CONFIG_NET_DSA_BCM_SF2 is not set +# CONFIG_NET_DSA_KS8995 is not set +# CONFIG_NET_DSA_LANTIQ_GSWIP is not set +# CONFIG_NET_DSA_LOOP is not set +# CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON is not set +# CONFIG_NET_DSA_MSCC_FELIX is not set +# CONFIG_NET_DSA_MSCC_OCELOT_EXT is not set +# CONFIG_NET_DSA_MSCC_SEVILLE is not set +# CONFIG_NET_DSA_MT7530 is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6XXX_LEDS is not set +# CONFIG_NET_DSA_MV88E6XXX_PTP is not set +# CONFIG_NET_DSA_QCA8K is not set +# CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT is not set +# CONFIG_NET_DSA_REALTEK is not set +# CONFIG_NET_DSA_REALTEK_SMI is not set +# CONFIG_NET_DSA_SJA1105 is not set +# CONFIG_NET_DSA_SMSC_LAN9303_I2C is not set +# CONFIG_NET_DSA_SMSC_LAN9303_MDIO is not set +# CONFIG_NET_DSA_TAG_AR9331 is not set +# CONFIG_NET_DSA_TAG_BRCM is not set +# CONFIG_NET_DSA_TAG_BRCM_LEGACY is not set +# CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS is not set +# CONFIG_NET_DSA_TAG_BRCM_PREPEND is not set +# CONFIG_NET_DSA_TAG_DSA is not set +# CONFIG_NET_DSA_TAG_EDSA is not set +# CONFIG_NET_DSA_TAG_GSWIP is not set +# CONFIG_NET_DSA_TAG_HELLCREEK is not set +# CONFIG_NET_DSA_TAG_KSZ is not set +# CONFIG_NET_DSA_TAG_LAN9303 is not set +# CONFIG_NET_DSA_TAG_MTK is not set +# CONFIG_NET_DSA_TAG_NONE is not set +# CONFIG_NET_DSA_TAG_OCELOT is not set +# CONFIG_NET_DSA_TAG_OCELOT_8021Q is not set +# CONFIG_NET_DSA_TAG_QCA is not set +# CONFIG_NET_DSA_TAG_RTL4_A is not set +# CONFIG_NET_DSA_TAG_RTL8_4 is not set +# CONFIG_NET_DSA_TAG_RZN1_A5PSW is not set +# CONFIG_NET_DSA_TAG_SJA1105 is not set +# CONFIG_NET_DSA_TAG_TRAILER is not set +# CONFIG_NET_DSA_TAG_VSC73XX_8021Q is not set +# CONFIG_NET_DSA_TAG_XRS700X is not set +# CONFIG_NET_DSA_VITESSE_VSC73XX is not set +# CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM is not set +# CONFIG_NET_DSA_VITESSE_VSC73XX_SPI is not set +# CONFIG_NET_DSA_XRS700X_I2C is not set +# CONFIG_NET_DSA_XRS700X_MDIO is not set +# CONFIG_NET_EMATCH is not set +# CONFIG_NET_EMATCH_CANID is not set +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_IPT is not set +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_TEXT is not set +# CONFIG_NET_EMATCH_U32 is not set +# CONFIG_NET_FAILOVER is not set +# CONFIG_NET_FC is not set +# CONFIG_NET_FOU is not set +# CONFIG_NET_FOU_IP_TUNNELS is not set +# CONFIG_NET_IFE is not set +# CONFIG_NET_IPGRE is not set +CONFIG_NET_IPGRE_BROADCAST=y +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPVTI is not set +# CONFIG_NET_IP_TUNNEL is not set +# CONFIG_NET_KEY is not set +# CONFIG_NET_KEY_MIGRATE is not set +# CONFIG_NET_L3_MASTER_DEV is not set +# CONFIG_NET_MEDIATEK_STAR_EMAC is not set +# CONFIG_NET_MPLS_GSO is not set +# CONFIG_NET_NCSI is not set +# CONFIG_NET_NSH is not set +# CONFIG_NET_NS_REFCNT_TRACKER is not set +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_NET_PTP_CLASSIFY is not set +CONFIG_NET_RX_BUSY_POLL=y +# CONFIG_NET_SB1000 is not set +CONFIG_NET_SCHED=y +# CONFIG_NET_SCH_CAKE is not set +# CONFIG_NET_SCH_CBS is not set +# CONFIG_NET_SCH_CHOKE is not set +# CONFIG_NET_SCH_CODEL is not set +CONFIG_NET_SCH_DEFAULT=y +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_ETF is not set +# CONFIG_NET_SCH_ETS is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_NET_SCH_FQ is not set +CONFIG_NET_SCH_FQ_CODEL=y +# CONFIG_NET_SCH_FQ_PIE is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_HHF is not set +# CONFIG_NET_SCH_HTB is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_MQPRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_PIE is not set +# CONFIG_NET_SCH_PLUG is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_QFQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFB is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_SKBPRIO is not set +# CONFIG_NET_SCH_TAPRIO is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SELFTESTS is not set +CONFIG_NET_SOCK_MSG=y +CONFIG_NET_SWITCHDEV=y +# CONFIG_NET_TC_SKB_EXT is not set +# CONFIG_NET_TEAM is not set +# CONFIG_NET_TEAM_MODE_ACTIVEBACKUP is not set +# CONFIG_NET_TEAM_MODE_BROADCAST is not set +# CONFIG_NET_TEAM_MODE_LOADBALANCE is not set +# CONFIG_NET_TEAM_MODE_RANDOM is not set +# CONFIG_NET_TEAM_MODE_ROUNDROBIN is not set +# CONFIG_NET_TULIP is not set +# CONFIG_NET_UDP_TUNNEL is not set +CONFIG_NET_VENDOR_3COM=y +CONFIG_NET_VENDOR_8390=y +CONFIG_NET_VENDOR_ADAPTEC=y +CONFIG_NET_VENDOR_ADI=y +CONFIG_NET_VENDOR_AGERE=y +CONFIG_NET_VENDOR_ALACRITECH=y +CONFIG_NET_VENDOR_ALTEON=y +CONFIG_NET_VENDOR_AMAZON=y +CONFIG_NET_VENDOR_AMD=y +CONFIG_NET_VENDOR_AQUANTIA=y +CONFIG_NET_VENDOR_ARC=y +# CONFIG_NET_VENDOR_ASIX is not set +CONFIG_NET_VENDOR_ATHEROS=y +CONFIG_NET_VENDOR_BROADCOM=y +CONFIG_NET_VENDOR_BROCADE=y +CONFIG_NET_VENDOR_CADENCE=y +CONFIG_NET_VENDOR_CAVIUM=y +CONFIG_NET_VENDOR_CHELSIO=y +CONFIG_NET_VENDOR_CIRRUS=y +CONFIG_NET_VENDOR_CISCO=y +CONFIG_NET_VENDOR_CORTINA=y +CONFIG_NET_VENDOR_DAVICOM=y +CONFIG_NET_VENDOR_DEC=y +CONFIG_NET_VENDOR_DLINK=y +CONFIG_NET_VENDOR_EMULEX=y +# CONFIG_NET_VENDOR_ENGLEDER is not set +CONFIG_NET_VENDOR_EZCHIP=y +CONFIG_NET_VENDOR_FARADAY=y +CONFIG_NET_VENDOR_FREESCALE=y +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_NET_VENDOR_FUNGIBLE is not set +CONFIG_NET_VENDOR_GOOGLE=y +CONFIG_NET_VENDOR_HISILICON=y +CONFIG_NET_VENDOR_HUAWEI=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_IBM=y +CONFIG_NET_VENDOR_INTEL=y +# CONFIG_NET_VENDOR_LITEX is not set +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_NET_VENDOR_MELLANOX=y +CONFIG_NET_VENDOR_META=y +CONFIG_NET_VENDOR_MICREL=y +CONFIG_NET_VENDOR_MICROCHIP=y +CONFIG_NET_VENDOR_MICROSEMI=y +# CONFIG_NET_VENDOR_MICROSOFT is not set +CONFIG_NET_VENDOR_MYRI=y +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NET_VENDOR_NETERION=y +CONFIG_NET_VENDOR_NETRONOME=y +CONFIG_NET_VENDOR_NI=y +CONFIG_NET_VENDOR_NVIDIA=y +CONFIG_NET_VENDOR_OKI=y +CONFIG_NET_VENDOR_PACKET_ENGINES=y +CONFIG_NET_VENDOR_PENSANDO=y +CONFIG_NET_VENDOR_QLOGIC=y +CONFIG_NET_VENDOR_QUALCOMM=y +CONFIG_NET_VENDOR_RDC=y +CONFIG_NET_VENDOR_REALTEK=y +CONFIG_NET_VENDOR_RENESAS=y +CONFIG_NET_VENDOR_ROCKER=y +CONFIG_NET_VENDOR_SAMSUNG=y +CONFIG_NET_VENDOR_SEEQ=y +CONFIG_NET_VENDOR_SILAN=y +CONFIG_NET_VENDOR_SIS=y +CONFIG_NET_VENDOR_SMSC=y +CONFIG_NET_VENDOR_SOCIONEXT=y +CONFIG_NET_VENDOR_SOLARFLARE=y +CONFIG_NET_VENDOR_STMICRO=y +CONFIG_NET_VENDOR_SUN=y +CONFIG_NET_VENDOR_SYNOPSYS=y +CONFIG_NET_VENDOR_TEHUTI=y +CONFIG_NET_VENDOR_TI=y +CONFIG_NET_VENDOR_TOSHIBA=y +# CONFIG_NET_VENDOR_VERTEXCOM is not set +CONFIG_NET_VENDOR_VIA=y +# CONFIG_NET_VENDOR_WANGXUN is not set +CONFIG_NET_VENDOR_WIZNET=y +CONFIG_NET_VENDOR_XILINX=y +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_NET_VRF is not set +# CONFIG_NET_XGENE is not set +CONFIG_NEW_LEDS=y +# CONFIG_NFC is not set +# CONFIG_NFP is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_LEGACY_CLIENT_TRACKING is not set +# CONFIG_NFSD_V2 is not set +# CONFIG_NFSD_V2_ACL is not set +# CONFIG_NFSD_V3_ACL is not set +# CONFIG_NFSD_V4 is not set +# CONFIG_NFS_ACL_SUPPORT is not set +CONFIG_NFS_COMMON=y +# CONFIG_NFS_DISABLE_UDP_SUPPORT is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_FSCACHE is not set +# CONFIG_NFS_LOCALIO is not set +# CONFIG_NFS_SWAP is not set +# CONFIG_NFS_V2 is not set +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_V4_1 is not set +# CONFIG_NFTL is not set +# CONFIG_NFT_BRIDGE_META is not set +# CONFIG_NFT_BRIDGE_REJECT is not set +# CONFIG_NFT_CONNLIMIT is not set +# CONFIG_NFT_DUP_IPV4 is not set +# CONFIG_NFT_DUP_IPV6 is not set +# CONFIG_NFT_FIB_IPV4 is not set +# CONFIG_NFT_FIB_IPV6 is not set +# CONFIG_NFT_FIB_NETDEV is not set +# CONFIG_NFT_FLOW_OFFLOAD is not set +# CONFIG_NFT_OSF is not set +# CONFIG_NFT_REJECT_NETDEV is not set +# CONFIG_NFT_SOCKET is not set +# CONFIG_NFT_SYNPROXY is not set +# CONFIG_NFT_TPROXY is not set +# CONFIG_NFT_TUNNEL is not set +# CONFIG_NFT_XFRM is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NF_CONNTRACK_AMANDA is not set +# CONFIG_NF_CONNTRACK_BRIDGE is not set +# CONFIG_NF_CONNTRACK_EVENTS is not set +# CONFIG_NF_CONNTRACK_FTP is not set +# CONFIG_NF_CONNTRACK_H323 is not set +# CONFIG_NF_CONNTRACK_IRC is not set +# CONFIG_NF_CONNTRACK_LABELS is not set +# CONFIG_NF_CONNTRACK_MARK is not set +# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set +# CONFIG_NF_CONNTRACK_PPTP is not set +CONFIG_NF_CONNTRACK_PROCFS=y +# CONFIG_NF_CONNTRACK_SANE is not set +# CONFIG_NF_CONNTRACK_SECMARK is not set +# CONFIG_NF_CONNTRACK_SIP is not set +# CONFIG_NF_CONNTRACK_SNMP is not set +# CONFIG_NF_CONNTRACK_TFTP is not set +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +# CONFIG_NF_CONNTRACK_ZONES is not set +# CONFIG_NF_CT_NETLINK is not set +# CONFIG_NF_CT_NETLINK_HELPER is not set +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +# CONFIG_NF_CT_PROTO_DCCP is not set +# CONFIG_NF_CT_PROTO_GRE is not set +# CONFIG_NF_CT_PROTO_SCTP is not set +# CONFIG_NF_CT_PROTO_UDPLITE is not set +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_NF_DUP_IPV4 is not set +# CONFIG_NF_DUP_IPV6 is not set +# CONFIG_NF_FLOW_TABLE is not set +# CONFIG_NF_FLOW_TABLE_PROCFS is not set +# CONFIG_NF_LOG_ARP is not set +# CONFIG_NF_LOG_IPV4 is not set +# CONFIG_NF_LOG_SYSLOG is not set +# CONFIG_NF_NAT is not set +# CONFIG_NF_NAT_AMANDA is not set +# CONFIG_NF_NAT_FTP is not set +# CONFIG_NF_NAT_H323 is not set +# CONFIG_NF_NAT_IRC is not set +# CONFIG_NF_NAT_PPTP is not set +# CONFIG_NF_NAT_SIP is not set +# CONFIG_NF_NAT_SNMP_BASIC is not set +# CONFIG_NF_NAT_TFTP is not set +# CONFIG_NF_REJECT_IPV4 is not set +# CONFIG_NF_REJECT_IPV6 is not set +# CONFIG_NF_SOCKET_IPV4 is not set +# CONFIG_NF_SOCKET_IPV6 is not set +# CONFIG_NF_TABLES is not set +CONFIG_NF_TABLES_ARP=y +CONFIG_NF_TABLES_BRIDGE=y +CONFIG_NF_TABLES_INET=y +CONFIG_NF_TABLES_IPV4=y +CONFIG_NF_TABLES_IPV6=y +CONFIG_NF_TABLES_NETDEV=y +# CONFIG_NF_TPROXY_IPV4 is not set +# CONFIG_NF_TPROXY_IPV6 is not set +# CONFIG_NGBE is not set +# CONFIG_NI903X_WDT is not set +# CONFIG_NIC7018_WDT is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_NIU is not set +# CONFIG_NI_XGE_MANAGEMENT_ENET is not set +CONFIG_NLATTR=y +# CONFIG_NLMON is not set +# CONFIG_NLS is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +# CONFIG_NLS_UCS2_UTILS is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_NOA1305 is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_NOZOMI is not set +# CONFIG_NO_HZ is not set +# CONFIG_NO_HZ_FULL is not set +# CONFIG_NO_HZ_IDLE is not set +# CONFIG_NS83820 is not set +# CONFIG_NTB is not set +# CONFIG_NTFS3_64BIT_CLUSTER is not set +# CONFIG_NTFS3_FS is not set +# CONFIG_NTFS3_FS_POSIX_ACL is not set +# CONFIG_NTFS3_LZX_XPRESS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTP_PPS is not set +# CONFIG_NULL_TTY is not set +# CONFIG_NUMA is not set +# CONFIG_NVGRACE_GPU_VFIO_PCI is not set +# CONFIG_NVIDIA_CARMEL_CNP_ERRATUM is not set +# CONFIG_NVIDIA_WMI_EC_BACKLIGHT is not set +# CONFIG_NVMEM is not set +# CONFIG_NVMEM_BCM_OCOTP is not set +# CONFIG_NVMEM_BLOCK is not set +# CONFIG_NVMEM_IMX_OCOTP is not set +# CONFIG_NVMEM_LAYOUT_ASCII_ENV is not set +# CONFIG_NVMEM_LAYOUT_MIKROTIK is not set +# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set +# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set +# CONFIG_NVMEM_LAYOUT_U_BOOT_ENV is not set +# CONFIG_NVMEM_REBOOT_MODE is not set +# CONFIG_NVMEM_RMEM is not set +# CONFIG_NVMEM_SYSFS is not set +# CONFIG_NVMEM_U_BOOT_ENV is not set +# CONFIG_NVME_AUTH is not set +# CONFIG_NVME_FC is not set +# CONFIG_NVME_HOST_AUTH is not set +# CONFIG_NVME_TARGET is not set +# CONFIG_NVME_TCP is not set +# CONFIG_NVME_VERBOSE_ERRORS is not set +# CONFIG_NVRAM is not set +# CONFIG_NV_TCO is not set +# CONFIG_NXP_C45_TJA11XX_PHY is not set +# CONFIG_NXP_CBTX_PHY is not set +# CONFIG_NXP_TJA11XX_PHY is not set +# CONFIG_N_GSM is not set +# CONFIG_OABI_COMPAT is not set +# CONFIG_OA_TC6 is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_OCTEONTX2_AF is not set +# CONFIG_OCTEONTX2_PF is not set +# CONFIG_OCTEON_EP is not set +# CONFIG_OCTEON_EP_VF is not set +# CONFIG_OF_OVERLAY is not set +CONFIG_OF_PARTITION=y +CONFIG_OF_RESERVED_MEM=y +# CONFIG_OF_UNITTEST is not set +# CONFIG_OID_REGISTRY is not set +# CONFIG_OMAP2_DSS_DEBUG is not set +# CONFIG_OMAP2_DSS_DEBUGFS is not set +# CONFIG_OMAP2_DSS_SDI is not set +# CONFIG_OMAP_OCP2SCP is not set +# CONFIG_OMAP_USB2 is not set +# CONFIG_OMFS_FS is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_OPEN_DICE is not set +# CONFIG_OPT3001 is not set +# CONFIG_OPT4001 is not set +# CONFIG_ORANGEFS_FS is not set +# CONFIG_ORION_WATCHDOG is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_OSNOISE_TRACER is not set +CONFIG_OVERLAY_FS=y +# CONFIG_OVERLAY_FS_DEBUG is not set +# CONFIG_OVERLAY_FS_INDEX is not set +# CONFIG_OVERLAY_FS_METACOPY is not set +CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y +# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set +CONFIG_OVERLAY_FS_XINO_AUTO=y +# CONFIG_P54_COMMON is not set +# CONFIG_PA12203001 is not set +# CONFIG_PAC1921 is not set +# CONFIG_PAC1934 is not set +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +# CONFIG_PACKING is not set +# CONFIG_PAGE_EXTENSION is not set +# CONFIG_PAGE_OWNER is not set +# CONFIG_PAGE_POISONING is not set +# CONFIG_PAGE_POOL is not set +# CONFIG_PAGE_POOL_STATS is not set +# CONFIG_PAGE_REPORTING is not set +CONFIG_PAGE_SHIFT=12 +# CONFIG_PAGE_SIZE_16KB is not set +# CONFIG_PAGE_SIZE_32KB is not set +CONFIG_PAGE_SIZE_4KB=y +# CONFIG_PAGE_SIZE_64KB is not set +# CONFIG_PAGE_SIZE_8KB is not set +# CONFIG_PAGE_TABLE_CHECK is not set +# CONFIG_PALMAS_GPADC is not set +# CONFIG_PANASONIC_LAPTOP is not set +# CONFIG_PANEL is not set +CONFIG_PANIC_ON_OOPS=y +CONFIG_PANIC_ON_OOPS_VALUE=1 +CONFIG_PANIC_TIMEOUT=1 +# CONFIG_PANTHERLORD_FF is not set +# CONFIG_PARAVIRT is not set +# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set +# CONFIG_PARPORT is not set +# CONFIG_PARPORT_1284 is not set +# CONFIG_PARPORT_GSC is not set +# CONFIG_PARPORT_PC is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_PATA_ACPI is not set +# CONFIG_PATA_ALI is not set +# CONFIG_PATA_AMD is not set +# CONFIG_PATA_ARASAN_CF is not set +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_ATP867X is not set +# CONFIG_PATA_CMD640_PCI is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CS5520 is not set +# CONFIG_PATA_CS5530 is not set +# CONFIG_PATA_CS5535 is not set +# CONFIG_PATA_CS5536 is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_IMX is not set +# CONFIG_PATA_ISAPNP is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_LEGACY is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_MPIIX is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NINJA32 is not set +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_NS87415 is not set +# CONFIG_PATA_OCTEON_CF is not set +# CONFIG_PATA_OF_PLATFORM is not set +# CONFIG_PATA_OLDPIIX is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PARPORT is not set +# CONFIG_PATA_PCMCIA is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_PDC_OLD is not set +# CONFIG_PATA_PLATFORM is not set +# CONFIG_PATA_QDI is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RDC is not set +# CONFIG_PATA_RZ1000 is not set +# CONFIG_PATA_SC1200 is not set +# CONFIG_PATA_SCH is not set +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_TOSHIBA is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set +# CONFIG_PATA_WINBOND_VLB is not set +# CONFIG_PC104 is not set +# CONFIG_PC300TOO is not set +# CONFIG_PCCARD is not set +# CONFIG_PCH_DMA is not set +# CONFIG_PCH_GBE is not set +# CONFIG_PCH_PHUB is not set +# CONFIG_PCI is not set +# CONFIG_PCI200SYN is not set +# CONFIG_PCIEAER is not set +# CONFIG_PCIEAER_INJECT is not set +# CONFIG_PCIEASPM is not set +# CONFIG_PCIEPORTBUS is not set +# CONFIG_PCIE_AL is not set +# CONFIG_PCIE_ALTERA is not set +# CONFIG_PCIE_ARMADA_8K is not set +CONFIG_PCIE_BUS_DEFAULT=y +# CONFIG_PCIE_BUS_PEER2PEER is not set +# CONFIG_PCIE_BUS_PERFORMANCE is not set +# CONFIG_PCIE_BUS_SAFE is not set +# CONFIG_PCIE_BUS_TUNE_OFF is not set +# CONFIG_PCIE_CADENCE_HOST is not set +# CONFIG_PCIE_CADENCE_PLAT_HOST is not set +# CONFIG_PCIE_DPC is not set +# CONFIG_PCIE_DW_PLAT is not set +# CONFIG_PCIE_DW_PLAT_HOST is not set +# CONFIG_PCIE_ECRC is not set +# CONFIG_PCIE_IPROC is not set +# CONFIG_PCIE_KIRIN is not set +# CONFIG_PCIE_LAYERSCAPE_GEN4 is not set +# CONFIG_PCIE_MEDIATEK_GEN3 is not set +# CONFIG_PCIE_MICROCHIP_HOST is not set +# CONFIG_PCIE_PTM is not set +# CONFIG_PCIE_XILINX is not set +# CONFIG_PCIPCWATCHDOG is not set +# CONFIG_PCI_CNB20LE_QUIRK is not set +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCI_DISABLE_COMMON_QUIRKS is not set +# CONFIG_PCI_DYNAMIC_OF_NODES is not set +# CONFIG_PCI_ENDPOINT is not set +# CONFIG_PCI_ENDPOINT_TEST is not set +# CONFIG_PCI_FTPCI100 is not set +# CONFIG_PCI_HISI is not set +# CONFIG_PCI_HOST_GENERIC is not set +# CONFIG_PCI_HOST_THUNDER_ECAM is not set +# CONFIG_PCI_HOST_THUNDER_PEM is not set +# CONFIG_PCI_IOV is not set +# CONFIG_PCI_J721E_HOST is not set +# CONFIG_PCI_LAYERSCAPE is not set +# CONFIG_PCI_MESON is not set +# CONFIG_PCI_MSI is not set +# CONFIG_PCI_NPEM is not set +# CONFIG_PCI_PASID is not set +# CONFIG_PCI_PF_STUB is not set +# CONFIG_PCI_PRI is not set +CONFIG_PCI_QUIRKS=y +# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set +# CONFIG_PCI_STUB is not set +# CONFIG_PCI_SW_SWITCHTEC is not set +CONFIG_PCI_SYSCALL=y +# CONFIG_PCI_V3_SEMI is not set +# CONFIG_PCI_XGENE is not set +# CONFIG_PCMCIA is not set +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_AXNET is not set +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_PCMCIA_LOAD_CIS is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_PCMCIA_PCNET is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SMC91C92 is not set +# CONFIG_PCMCIA_SYM53C500 is not set +# CONFIG_PCMCIA_XIRC2PS is not set +# CONFIG_PCMCIA_XIRCOM is not set +# CONFIG_PCNET32 is not set +CONFIG_PCPU_DEV_REFCNT=y +CONFIG_PCP_BATCH_SCALE_MAX=5 +# CONFIG_PCSPKR_PLATFORM is not set +# CONFIG_PCS_MTK_USXGMII is not set +# CONFIG_PCS_XPCS is not set +# CONFIG_PD6729 is not set +# CONFIG_PDC_ADMA is not set +# CONFIG_PDS_CORE is not set +# CONFIG_PECI is not set +# CONFIG_PERCPU_STATS is not set +# CONFIG_PERCPU_TEST is not set +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_EVENTS_AMD_POWER is not set +# CONFIG_PERSISTENT_KEYRINGS is not set +# CONFIG_PER_VMA_LOCK_STATS is not set +# CONFIG_PFCP is not set +# CONFIG_PHANTOM is not set +# CONFIG_PHONET is not set +# CONFIG_PHYLIB is not set +# CONFIG_PHYLIB_LEDS is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +# CONFIG_PHY_BRCM_USB is not set +# CONFIG_PHY_CADENCE_DPHY is not set +# CONFIG_PHY_CADENCE_DPHY_RX is not set +# CONFIG_PHY_CADENCE_SALVO is not set +# CONFIG_PHY_CADENCE_SIERRA is not set +# CONFIG_PHY_CADENCE_TORRENT is not set +# CONFIG_PHY_CAN_TRANSCEIVER is not set +# CONFIG_PHY_CPCAP_USB is not set +# CONFIG_PHY_EXYNOS_DP_VIDEO is not set +# CONFIG_PHY_EXYNOS_MIPI_VIDEO is not set +# CONFIG_PHY_FSL_IMX8MQ_USB is not set +# CONFIG_PHY_INGENIC_USB is not set +# CONFIG_PHY_INTEL_KEEMBAY_EMMC is not set +# CONFIG_PHY_LAN966X_SERDES is not set +# CONFIG_PHY_MAPPHONE_MDM6600 is not set +# CONFIG_PHY_MIXEL_MIPI_DPHY is not set +# CONFIG_PHY_MTK_HDMI is not set +# CONFIG_PHY_MTK_MIPI_DSI is not set +# CONFIG_PHY_MTK_XFI_TPHY is not set +# CONFIG_PHY_MVEBU_CP110_UTMI is not set +# CONFIG_PHY_OCELOT_SERDES is not set +# CONFIG_PHY_PISTACHIO_USB is not set +# CONFIG_PHY_PXA_28NM_HSIC is not set +# CONFIG_PHY_PXA_28NM_USB2 is not set +# CONFIG_PHY_QCOM_USB_HS is not set +# CONFIG_PHY_QCOM_USB_HSIC is not set +# CONFIG_PHY_SAMSUNG_USB2 is not set +# CONFIG_PHY_TUSB1210 is not set +# CONFIG_PHY_XGENE is not set +# CONFIG_PID_IN_CONTEXTIDR is not set +# CONFIG_PID_NS is not set +CONFIG_PINCONF=y +# CONFIG_PINCTRL is not set +# CONFIG_PINCTRL_AMD is not set +# CONFIG_PINCTRL_AW9523 is not set +# CONFIG_PINCTRL_AXP209 is not set +# CONFIG_PINCTRL_CEDARFORK is not set +# CONFIG_PINCTRL_CY8C95X0 is not set +# CONFIG_PINCTRL_EXYNOS is not set +# CONFIG_PINCTRL_ICELAKE is not set +# CONFIG_PINCTRL_IMX_SCMI is not set +# CONFIG_PINCTRL_INGENIC is not set +# CONFIG_PINCTRL_LPASS_LPI is not set +# CONFIG_PINCTRL_MCP23S08 is not set +# CONFIG_PINCTRL_MDM9607 is not set +# CONFIG_PINCTRL_MICROCHIP_SGPIO is not set +# CONFIG_PINCTRL_MSM8953 is not set +# CONFIG_PINCTRL_MSM8X74 is not set +# CONFIG_PINCTRL_MT6779 is not set +# CONFIG_PINCTRL_MT8167 is not set +# CONFIG_PINCTRL_MT8192 is not set +# CONFIG_PINCTRL_MT8195 is not set +# CONFIG_PINCTRL_MT8365 is not set +# CONFIG_PINCTRL_MTK_V2 is not set +# CONFIG_PINCTRL_OCELOT is not set +# CONFIG_PINCTRL_PISTACHIO is not set +# CONFIG_PINCTRL_SC7280 is not set +# CONFIG_PINCTRL_SC8180X is not set +# CONFIG_PINCTRL_SDX55 is not set +CONFIG_PINCTRL_SINGLE=y +# CONFIG_PINCTRL_SM6115 is not set +# CONFIG_PINCTRL_SM6125 is not set +# CONFIG_PINCTRL_SM8350 is not set +# CONFIG_PINCTRL_STMFX is not set +# CONFIG_PINCTRL_SX150X is not set +# CONFIG_PING is not set +CONFIG_PINMUX=y +# CONFIG_PKCS7_MESSAGE_PARSER is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_PL320_MBOX is not set +# CONFIG_PL330_DMA is not set +# CONFIG_PLATFORM_MHU is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_PLIP is not set +# CONFIG_PLX_DMA is not set +# CONFIG_PM is not set +# CONFIG_PMBUS is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMS7003 is not set +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_DEBUG is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_PM_USERSPACE_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_POSIX_MQUEUE is not set +CONFIG_POSIX_TIMERS=y +# CONFIG_POWERCAP is not set +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_RESET_BRCMKONA is not set +# CONFIG_POWER_RESET_BRCMSTB is not set +# CONFIG_POWER_RESET_GPIO is not set +# CONFIG_POWER_RESET_GPIO_RESTART is not set +# CONFIG_POWER_RESET_LINKSTATION is not set +# CONFIG_POWER_RESET_LTC2952 is not set +# CONFIG_POWER_RESET_PIIX4_POWEROFF is not set +# CONFIG_POWER_RESET_QNAP is not set +# CONFIG_POWER_RESET_REGULATOR is not set +# CONFIG_POWER_RESET_RESTART is not set +# CONFIG_POWER_RESET_SYSCON is not set +# CONFIG_POWER_RESET_SYSCON_POWEROFF is not set +# CONFIG_POWER_RESET_VERSATILE is not set +# CONFIG_POWER_RESET_XGENE is not set +# CONFIG_POWER_SEQUENCING is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_POWER_SUPPLY_HWMON is not set +# CONFIG_PPC4xx_GPIO is not set +# CONFIG_PPC_16K_PAGES is not set +# CONFIG_PPC_256K_PAGES is not set +CONFIG_PPC_4K_PAGES=y +# CONFIG_PPC_64K_PAGES is not set +# CONFIG_PPC_DISABLE_WERROR is not set +# CONFIG_PPC_EMULATED_STATS is not set +# CONFIG_PPC_EPAPR_HV_BYTECHAN is not set +# CONFIG_PPC_QUEUED_SPINLOCKS is not set +# CONFIG_PPP is not set +# CONFIG_PPPOATM is not set +# CONFIG_PPPOE is not set +# CONFIG_PPPOE_HASH_BITS_1 is not set +# CONFIG_PPPOE_HASH_BITS_2 is not set +CONFIG_PPPOE_HASH_BITS_4=y +# CONFIG_PPPOE_HASH_BITS_8 is not set +# CONFIG_PPPOL2TP is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPP_DEFLATE is not set +CONFIG_PPP_FILTER=y +# CONFIG_PPP_MPPE is not set +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPS is not set +# CONFIG_PPS_CLIENT_GPIO is not set +# CONFIG_PPS_CLIENT_KTIMER is not set +# CONFIG_PPS_CLIENT_LDISC is not set +# CONFIG_PPS_CLIENT_PARPORT is not set +# CONFIG_PPS_DEBUG is not set +# CONFIG_PPTP is not set +# CONFIG_PREEMPT is not set +# CONFIG_PREEMPTIRQ_DELAY_TEST is not set +# CONFIG_PREEMPT_DYNAMIC is not set +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_NONE_BUILD=y +# CONFIG_PREEMPT_RT is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PRESTERA is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_PRIME_NUMBERS is not set +CONFIG_PRINTK=y +# CONFIG_PRINTK_CALLER is not set +# CONFIG_PRINTK_INDEX is not set +# CONFIG_PRINTK_TIME is not set +CONFIG_PRINT_STACK_DEPTH=64 +# CONFIG_PROC_CHILDREN is not set +CONFIG_PROC_FS=y +# CONFIG_PROC_KCORE is not set +CONFIG_PROC_MEM_ALWAYS_FORCE=y +# CONFIG_PROC_MEM_FORCE_PTRACE is not set +# CONFIG_PROC_MEM_NO_FORCE is not set +# CONFIG_PROC_PAGE_MONITOR is not set +# CONFIG_PROC_STRIPPED is not set +CONFIG_PROC_SYSCTL=y +# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILING is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_PROVE_RAW_LOCK_NESTING is not set +# CONFIG_PROVE_RCU is not set +# CONFIG_PROVE_RCU_LIST is not set +# CONFIG_PSAMPLE is not set +# CONFIG_PSB6970_PHY is not set +# CONFIG_PSE_CONTROLLER is not set +# CONFIG_PSE_PD692X0 is not set +# CONFIG_PSE_REGULATOR is not set +# CONFIG_PSE_SI3474 is not set +# CONFIG_PSE_TPS23881 is not set +# CONFIG_PSI is not set +# CONFIG_PSI_DEFAULT_DISABLED is not set +# CONFIG_PSTORE is not set +# CONFIG_PSTORE_BLK is not set +# CONFIG_PSTORE_COMPRESS is not set +# CONFIG_PSTORE_CONSOLE is not set +CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240 +# CONFIG_PSTORE_FTRACE is not set +# CONFIG_PSTORE_PMSG is not set +# CONFIG_PSTORE_RAM is not set +# CONFIG_PTDUMP_DEBUGFS is not set +# CONFIG_PTP_1588_CLOCK is not set +# CONFIG_PTP_1588_CLOCK_FC3W is not set +# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set +# CONFIG_PTP_1588_CLOCK_IDTCM is not set +# CONFIG_PTP_1588_CLOCK_IXP46X is not set +# CONFIG_PTP_1588_CLOCK_KVM is not set +# CONFIG_PTP_1588_CLOCK_MOCK is not set +# CONFIG_PTP_1588_CLOCK_OCP is not set +# CONFIG_PTP_1588_CLOCK_PCH is not set +# CONFIG_PTP_1588_CLOCK_VMW is not set +# CONFIG_PVPANIC is not set +# CONFIG_PWM is not set +# CONFIG_PWM_ATMEL_TCB is not set +# CONFIG_PWM_CLK is not set +# CONFIG_PWM_DEBUG is not set +# CONFIG_PWM_DWC is not set +# CONFIG_PWM_FSL_FTM is not set +# CONFIG_PWM_GPIO is not set +# CONFIG_PWM_IMG is not set +# CONFIG_PWM_JZ4740 is not set +# CONFIG_PWM_MEDIATEK is not set +# CONFIG_PWM_PCA9685 is not set +# CONFIG_PWM_RASPBERRYPI_POE is not set +# CONFIG_PWM_XILINX is not set +CONFIG_PWRSEQ_EMMC=y +# CONFIG_PWRSEQ_SD8787 is not set +CONFIG_PWRSEQ_SIMPLE=y +# CONFIG_QCA7000 is not set +# CONFIG_QCA7000_SPI is not set +# CONFIG_QCA7000_UART is not set +# CONFIG_QCA807X_PHY is not set +# CONFIG_QCA808X_PHY is not set +# CONFIG_QCA83XX_PHY is not set +# CONFIG_QCOM_A7PLL is not set +# CONFIG_QCOM_BAM_DMUX is not set +# CONFIG_QCOM_EMAC is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set +# CONFIG_QCOM_GPI_DMA is not set +# CONFIG_QCOM_HIDMA is not set +# CONFIG_QCOM_HIDMA_MGMT is not set +# CONFIG_QCOM_LMH is not set +# CONFIG_QCOM_PBS is not set +# CONFIG_QCOM_PD_MAPPER is not set +# CONFIG_QCOM_PMIC_PDCHARGER_ULOG is not set +# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set +# CONFIG_QCOM_SPMI_ADC5 is not set +# CONFIG_QCOM_SPMI_ADC_TM5 is not set +# CONFIG_QCOM_SPMI_IADC is not set +# CONFIG_QCOM_SPMI_TEMP_ALARM is not set +# CONFIG_QCOM_SPMI_VADC is not set +# CONFIG_QCOM_SSC_BLOCK_BUS is not set +# CONFIG_QED is not set +# CONFIG_QFMT_V1 is not set +# CONFIG_QLA3XXX is not set +# CONFIG_QLCNIC is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_QORIQ_CPUFREQ is not set +# CONFIG_QORIQ_THERMAL is not set +# CONFIG_QRTR is not set +# CONFIG_QRTR_MHI is not set +# CONFIG_QRTR_TUN is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_QUICC_ENGINE is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_QUOTA_DEBUG is not set +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +# CONFIG_R6040 is not set +# CONFIG_R8169 is not set +# CONFIG_R8169_LEDS is not set +# CONFIG_R8712U is not set +# CONFIG_RADIO_ADAPTERS is not set +# CONFIG_RADIO_AZTECH is not set +# CONFIG_RADIO_CADET is not set +# CONFIG_RADIO_GEMTEK is not set +# CONFIG_RADIO_MAXIRADIO is not set +# CONFIG_RADIO_RTRACK is not set +# CONFIG_RADIO_RTRACK2 is not set +# CONFIG_RADIO_SF16FMI is not set +# CONFIG_RADIO_SF16FMR2 is not set +# CONFIG_RADIO_TERRATEC is not set +# CONFIG_RADIO_TRUST is not set +# CONFIG_RADIO_TYPHOON is not set +# CONFIG_RADIO_ZOLTRIX is not set +# CONFIG_RAID6_PQ_BENCHMARK is not set +# CONFIG_RAID_ATTRS is not set +# CONFIG_RALINK is not set +# CONFIG_RANDOM32_SELFTEST is not set +# CONFIG_RANDOMIZE_BASE is not set +CONFIG_RANDOMIZE_KSTACK_OFFSET=y +# CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT is not set +# CONFIG_RANDOM_KMALLOC_CACHES is not set +# CONFIG_RANDSTRUCT_NONE is not set +# CONFIG_RAPIDIO is not set +# CONFIG_RAS is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_RCU_BOOST is not set +# CONFIG_RCU_CPU_STALL_CPUTIME is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_EQS_DEBUG is not set +# CONFIG_RCU_EXPERT is not set +CONFIG_RCU_EXP_CPU_STALL_TIMEOUT=0 +CONFIG_RCU_NEED_SEGCBLIST=y +# CONFIG_RCU_REF_SCALE_TEST is not set +# CONFIG_RCU_SCALE_TEST is not set +CONFIG_RCU_STALL_COMMON=y +# CONFIG_RCU_STRICT_GRACE_PERIOD is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_RC_ATI_REMOTE is not set +# CONFIG_RC_CORE is not set +# CONFIG_RC_DECODERS is not set +# CONFIG_RC_LOOPBACK is not set +# CONFIG_RC_MAP is not set +# CONFIG_RC_XBOX_DVD is not set +# CONFIG_RDS is not set +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_GZIP is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_ZSTD is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_READ_ONLY_THP_FOR_FS is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_REDWOOD is not set +# CONFIG_REED_SOLOMON is not set +# CONFIG_REED_SOLOMON_DEC8 is not set +# CONFIG_REED_SOLOMON_ENC8 is not set +# CONFIG_REED_SOLOMON_TEST is not set +# CONFIG_REGMAP is not set +# CONFIG_REGMAP_I2C is not set +# CONFIG_REGMAP_MMIO is not set +# CONFIG_REGMAP_SPI is not set +# CONFIG_REGULATOR is not set +# CONFIG_REGULATOR_88PG86X is not set +# CONFIG_REGULATOR_ACT8865 is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_ANATOP is not set +# CONFIG_REGULATOR_AW37503 is not set +# CONFIG_REGULATOR_DA9121 is not set +# CONFIG_REGULATOR_DA9210 is not set +# CONFIG_REGULATOR_DA9211 is not set +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_FAN53555 is not set +# CONFIG_REGULATOR_FAN53880 is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_GPIO is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_ISL9305 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_LTC3589 is not set +# CONFIG_REGULATOR_LTC3676 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX20086 is not set +# CONFIG_REGULATOR_MAX20411 is not set +# CONFIG_REGULATOR_MAX77503 is not set +# CONFIG_REGULATOR_MAX77620 is not set +# CONFIG_REGULATOR_MAX77826 is not set +# CONFIG_REGULATOR_MAX77857 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8893 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_MCP16502 is not set +# CONFIG_REGULATOR_MP5416 is not set +# CONFIG_REGULATOR_MP8859 is not set +# CONFIG_REGULATOR_MP886X is not set +# CONFIG_REGULATOR_MPQ7920 is not set +# CONFIG_REGULATOR_MT6311 is not set +# CONFIG_REGULATOR_MT6315 is not set +# CONFIG_REGULATOR_MT6359 is not set +# CONFIG_REGULATOR_NETLINK_EVENTS is not set +# CONFIG_REGULATOR_PCA9450 is not set +# CONFIG_REGULATOR_PF8X00 is not set +# CONFIG_REGULATOR_PFUZE100 is not set +# CONFIG_REGULATOR_PV88060 is not set +# CONFIG_REGULATOR_PV88080 is not set +# CONFIG_REGULATOR_PV88090 is not set +# CONFIG_REGULATOR_PWM is not set +# CONFIG_REGULATOR_QCOM_LABIBB is not set +# CONFIG_REGULATOR_QCOM_REFGEN is not set +# CONFIG_REGULATOR_QCOM_SPMI is not set +# CONFIG_REGULATOR_QCOM_USB_VBUS is not set +# CONFIG_REGULATOR_RAA215300 is not set +# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set +# CONFIG_REGULATOR_RT4801 is not set +# CONFIG_REGULATOR_RT4803 is not set +# CONFIG_REGULATOR_RT5190A is not set +# CONFIG_REGULATOR_RT5739 is not set +# CONFIG_REGULATOR_RT5759 is not set +# CONFIG_REGULATOR_RT6160 is not set +# CONFIG_REGULATOR_RT6190 is not set +# CONFIG_REGULATOR_RT6245 is not set +# CONFIG_REGULATOR_RTMV20 is not set +# CONFIG_REGULATOR_RTQ2134 is not set +# CONFIG_REGULATOR_RTQ2208 is not set +# CONFIG_REGULATOR_RTQ6752 is not set +# CONFIG_REGULATOR_SLG51000 is not set +# CONFIG_REGULATOR_SY8106A is not set +# CONFIG_REGULATOR_SY8824X is not set +# CONFIG_REGULATOR_SY8827N is not set +# CONFIG_REGULATOR_TI_ABB is not set +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS6286X is not set +# CONFIG_REGULATOR_TPS6287X is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS65132 is not set +# CONFIG_REGULATOR_TPS6524X is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_VCTRL is not set +# CONFIG_REGULATOR_VEXPRESS is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_FS_POSIX_ACL is not set +# CONFIG_REISERFS_FS_SECURITY is not set +CONFIG_REISERFS_FS_XATTR=y +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_RELAY is not set +# CONFIG_RELOCATABLE is not set +CONFIG_RELR=y +# CONFIG_REMOTEPROC is not set +# CONFIG_RENESAS_PHY is not set +# CONFIG_RESET_ATH79 is not set +# CONFIG_RESET_BERLIN is not set +# CONFIG_RESET_BRCMSTB is not set +# CONFIG_RESET_BRCMSTB_RESCAL is not set +# CONFIG_RESET_CONTROLLER is not set +# CONFIG_RESET_GPIO is not set +# CONFIG_RESET_IMX7 is not set +# CONFIG_RESET_INTEL_GW is not set +# CONFIG_RESET_LANTIQ is not set +# CONFIG_RESET_LPC18XX is not set +# CONFIG_RESET_MESON is not set +# CONFIG_RESET_PISTACHIO is not set +# CONFIG_RESET_SIMPLE is not set +# CONFIG_RESET_SOCFPGA is not set +# CONFIG_RESET_SUNXI is not set +# CONFIG_RESET_TEGRA_BPMP is not set +# CONFIG_RESET_TI_SYSCON is not set +# CONFIG_RESET_TI_TPS380X is not set +# CONFIG_RESET_ZYNQ is not set +# CONFIG_RFD77402 is not set +# CONFIG_RFD_FTL is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_FULL is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_LEDS is not set +# CONFIG_RICHTEK_RTQ6056 is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_RING_BUFFER_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set +# CONFIG_RISCV_PMU is not set +# CONFIG_RISCV_PMU_LEGACY is not set +# CONFIG_RISCV_PMU_SBI is not set +# CONFIG_RMI4_CORE is not set +# CONFIG_RMNET is not set +# CONFIG_ROCKCHIP_ERRATUM_3588001 is not set +# CONFIG_ROCKCHIP_PHY is not set +# CONFIG_ROCKER is not set +# CONFIG_ROHM_BM1390 is not set +# CONFIG_ROHM_BU27008 is not set +# CONFIG_ROHM_BU27034 is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_ROSE is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1 is not set +# CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2 is not set +# CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA is not set +# CONFIG_RPMB is not set +# CONFIG_RPMSG_QCOM_GLINK_RPM is not set +# CONFIG_RPMSG_VIRTIO is not set +# CONFIG_RPMSG_WWAN_CTRL is not set +# CONFIG_RPR0521 is not set +# CONFIG_RSEQ is not set +# CONFIG_RT2X00 is not set +# CONFIG_RTASE is not set +# CONFIG_RTC_CLASS is not set +# CONFIG_RTC_DEBUG is not set +# CONFIG_RTC_DRV_ABB5ZES3 is not set +# CONFIG_RTC_DRV_ABEOZ9 is not set +# CONFIG_RTC_DRV_ABX80X is not set +# CONFIG_RTC_DRV_ARMADA38X is not set +# CONFIG_RTC_DRV_AU1XXX is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_CADENCE is not set +CONFIG_RTC_DRV_CMOS=y +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1302 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1307_CENTURY is not set +# CONFIG_RTC_DRV_DS1343 is not set +# CONFIG_RTC_DRV_DS1347 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS1685_FAMILY is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS2404 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_EFI is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_EP93XX is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_FTRTC010 is not set +# CONFIG_RTC_DRV_GENERIC is not set +# CONFIG_RTC_DRV_GOLDFISH is not set +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +# CONFIG_RTC_DRV_HYM8563 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_ISL12026 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_JZ4740 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_MAX31335 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_MAX6916 is not set +# CONFIG_RTC_DRV_MAX77686 is not set +# CONFIG_RTC_DRV_MCP795 is not set +# CONFIG_RTC_DRV_MOXART is not set +# CONFIG_RTC_DRV_MPC5121 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_MT2712 is not set +# CONFIG_RTC_DRV_NCT3018Y is not set +# CONFIG_RTC_DRV_OMAP is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_PCF2127 is not set +# CONFIG_RTC_DRV_PCF85063 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF85363 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_PL030 is not set +# CONFIG_RTC_DRV_PL031 is not set +# CONFIG_RTC_DRV_PS3 is not set +# CONFIG_RTC_DRV_R7301 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_RV3028 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set +# CONFIG_RTC_DRV_RV3032 is not set +# CONFIG_RTC_DRV_RV8803 is not set +# CONFIG_RTC_DRV_RX4581 is not set +# CONFIG_RTC_DRV_RX6110 is not set +# CONFIG_RTC_DRV_RX8010 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_RX8111 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_SD2405AL is not set +# CONFIG_RTC_DRV_SD3078 is not set +# CONFIG_RTC_DRV_SNVS is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_SUN6I is not set +# CONFIG_RTC_DRV_TEGRA is not set +# CONFIG_RTC_DRV_TEST is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_XGENE is not set +# CONFIG_RTC_DRV_ZYNQMP is not set +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_LIB=y +# CONFIG_RTC_NVMEM is not set +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_SYSTOHC_DEVICE="rtc0" +# CONFIG_RTL8180 is not set +# CONFIG_RTL8187 is not set +# CONFIG_RTL8192E is not set +# CONFIG_RTL8261N_PHY is not set +# CONFIG_RTL8306_PHY is not set +# CONFIG_RTL8366RB_PHY is not set +# CONFIG_RTL8366S_PHY is not set +# CONFIG_RTL8366_SMI is not set +# CONFIG_RTL8366_SMI_DEBUG_FS is not set +# CONFIG_RTL8367B_PHY is not set +# CONFIG_RTL8367_PHY is not set +# CONFIG_RTLLIB is not set +# CONFIG_RTL_CARDS is not set +# CONFIG_RTS5208 is not set +CONFIG_RT_MUTEXES=y +CONFIG_RUNTIME_TESTING_MENU=y +# CONFIG_RUST is not set +# CONFIG_RV is not set +CONFIG_RXKAD=y +# CONFIG_RXPERF is not set +# CONFIG_S2IO is not set +# CONFIG_SAMPLES is not set +# CONFIG_SAMSUNG_LAPTOP is not set +# CONFIG_SATA_ACARD_AHCI is not set +# CONFIG_SATA_AHCI is not set +# CONFIG_SATA_AHCI_PLATFORM is not set +# CONFIG_SATA_DWC is not set +# CONFIG_SATA_DWC_OLD_DMA is not set +# CONFIG_SATA_FSL is not set +# CONFIG_SATA_HIGHBANK is not set +# CONFIG_SATA_HOST is not set +# CONFIG_SATA_INIC162X is not set +CONFIG_SATA_MOBILE_LPM_POLICY=0 +# CONFIG_SATA_MV is not set +# CONFIG_SATA_NV is not set +# CONFIG_SATA_PMP is not set +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_RCAR is not set +# CONFIG_SATA_SIL is not set +# CONFIG_SATA_SIL24 is not set +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_SVW is not set +# CONFIG_SATA_SX4 is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set +# CONFIG_SBC_FITPC2_WATCHDOG is not set +CONFIG_SBITMAP=y +# CONFIG_SC92031 is not set +# CONFIG_SCA3000 is not set +# CONFIG_SCA3300 is not set +# CONFIG_SCACHE_DEBUGFS is not set +# CONFIG_SCC is not set +# CONFIG_SCD30_CORE is not set +# CONFIG_SCD4X is not set +# CONFIG_SCF_TORTURE_TEST is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SCHED_CLASS_EXT is not set +# CONFIG_SCHED_CLUSTER is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHED_HRTICK=y +# CONFIG_SCHED_MC is not set +CONFIG_SCHED_OMIT_FRAME_POINTER=y +# CONFIG_SCHED_SMT is not set +CONFIG_SCHED_STACK_END_CHECK=y +# CONFIG_SCHED_TRACER is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_3W_SAS is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC94XX is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_ARCMSR is not set +# CONFIG_SCSI_BFA_FC is not set +# CONFIG_SCSI_BNX2X_FCOE is not set +# CONFIG_SCSI_BNX2_ISCSI is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CHELSIO_FCOE is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_CXGB3_ISCSI is not set +# CONFIG_SCSI_CXGB4_ISCSI is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_ESAS2R is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_FDOMAIN_PCI is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_HISI_SAS is not set +# CONFIG_SCSI_HPSA is not set +# CONFIG_SCSI_HPTIOP is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_ISCI is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_LOGGING is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set +# CONFIG_SCSI_LPFC is not set +CONFIG_SCSI_MOD=y +# CONFIG_SCSI_MPI3MR is not set +# CONFIG_SCSI_MPT2SAS is not set +# CONFIG_SCSI_MPT3SAS is not set +# CONFIG_SCSI_MVSAS is not set +# CONFIG_SCSI_MVSAS_DEBUG is not set +# CONFIG_SCSI_MVUMI is not set +# CONFIG_SCSI_MYRB is not set +# CONFIG_SCSI_MYRS is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_PM8001 is not set +# CONFIG_SCSI_PMCRAID is not set +CONFIG_SCSI_PROC_FS=y +# CONFIG_SCSI_QLA_FC is not set +# CONFIG_SCSI_QLA_ISCSI is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +# CONFIG_SCSI_SMARTPQI is not set +# CONFIG_SCSI_SNIC is not set +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# 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 +# CONFIG_SC_DISPCC_7280 is not set +# CONFIG_SC_GCC_7280 is not set +# CONFIG_SC_GCC_8180X is not set +# CONFIG_SC_GPUCC_7280 is not set +# CONFIG_SC_GPUCC_8280XP is not set +# CONFIG_SC_VIDEOCC_7280 is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_SDIO_UART is not set +# CONFIG_SDM_GPUCC_660 is not set +# CONFIG_SDM_MMCC_660 is not set +# CONFIG_SDP500 is not set +# CONFIG_SDR_MAX2175 is not set +# CONFIG_SDR_PLATFORM_DRIVERS is not set +# CONFIG_SDX_GCC_55 is not set +# CONFIG_SD_ADC_MODULATOR is not set +# CONFIG_SECCOMP is not set +# CONFIG_SECCOMP_CACHE_DEBUG is not set +# CONFIG_SECRETMEM is not set +CONFIG_SECTION_MISMATCH_WARN_ONLY=y +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_APPARMOR is not set +CONFIG_SECURITY_DMESG_RESTRICT=y +# CONFIG_SECURITY_LANDLOCK is not set +# CONFIG_SECURITY_LOADPIN is not set +# CONFIG_SECURITY_LOCKDOWN_LSM is not set +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_SAFESETID is not set +# CONFIG_SECURITY_SELINUX_AVC_STATS is not set +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +# CONFIG_SECURITY_SELINUX_DEBUG is not set +# CONFIG_SECURITY_SELINUX_DEVELOP is not set +# CONFIG_SECURITY_SMACK is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_YAMA is not set +CONFIG_SELECT_MEMORY_MODEL=y +# CONFIG_SENSEAIR_SUNRISE_CO2 is not set +# CONFIG_SENSIRION_SGP30 is not set +# CONFIG_SENSIRION_SGP40 is not set +# CONFIG_SENSORS_ABITUGURU is not set +# CONFIG_SENSORS_ABITUGURU3 is not set +# CONFIG_SENSORS_ACBEL_FSG032 is not set +# CONFIG_SENSORS_ACPI_POWER is not set +# CONFIG_SENSORS_AD7314 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADC128D818 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM1177 is not set +# CONFIG_SENSORS_ADM1266 is not set +# CONFIG_SENSORS_ADM1275 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADP1050 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADS7871 is not set +# CONFIG_SENSORS_ADT7310 is not set +# CONFIG_SENSORS_ADT7410 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_AHT10 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_SENSORS_APPLESMC is not set +# CONFIG_SENSORS_AQUACOMPUTER_D5NEXT is not set +# CONFIG_SENSORS_AS370 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ASPEED is not set +# CONFIG_SENSORS_ASUS_ROG_RYUJIN is not set +# CONFIG_SENSORS_ASUS_WMI is not set +# CONFIG_SENSORS_ATK0110 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_AXI_FAN_CONTROL is not set +# CONFIG_SENSORS_BEL_PFE is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_BPA_RS600 is not set +# CONFIG_SENSORS_CHIPCAP2 is not set +# CONFIG_SENSORS_CORETEMP is not set +# CONFIG_SENSORS_CORSAIR_CPRO is not set +# CONFIG_SENSORS_CORSAIR_PSU is not set +# CONFIG_SENSORS_DELL_SMM is not set +# CONFIG_SENSORS_DELTA_AHE50DC_FAN is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_DPS920AB is not set +# CONFIG_SENSORS_DRIVETEMP is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC2305 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_FAM15H_POWER is not set +# CONFIG_SENSORS_FSCHMD is not set +# CONFIG_SENSORS_FSP_3Y is not set +# CONFIG_SENSORS_FTSTEUTATES is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_G762 is not set +# CONFIG_SENSORS_GIGABYTE_WATERFORCE is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_GSC is not set +# CONFIG_SENSORS_HDAPS is not set +# CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_HMC5843 is not set +# CONFIG_SENSORS_HMC5843_I2C is not set +# CONFIG_SENSORS_HMC5843_SPI is not set +# CONFIG_SENSORS_HP_WMI is not set +# CONFIG_SENSORS_HS3001 is not set +# CONFIG_SENSORS_I5500 is not set +# CONFIG_SENSORS_I5K_AMB is not set +# CONFIG_SENSORS_IBM_CFFPS is not set +# CONFIG_SENSORS_IIO_HWMON is not set +# CONFIG_SENSORS_INA209 is not set +# CONFIG_SENSORS_INA238 is not set +# CONFIG_SENSORS_INA2XX is not set +# CONFIG_SENSORS_INA3221 is not set +# CONFIG_SENSORS_INSPUR_IPSPS is not set +# CONFIG_SENSORS_IR35221 is not set +# CONFIG_SENSORS_IR36021 is not set +# CONFIG_SENSORS_IR38064 is not set +# CONFIG_SENSORS_IRPS5401 is not set +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_SENSORS_ISL68137 is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_K10TEMP is not set +# CONFIG_SENSORS_K8TEMP is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_SENSORS_LIS3_I2C is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LM25066 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LM95234 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_LT7182S is not set +# CONFIG_SENSORS_LTC2945 is not set +# CONFIG_SENSORS_LTC2947_I2C is not set +# CONFIG_SENSORS_LTC2947_SPI is not set +# CONFIG_SENSORS_LTC2978 is not set +# CONFIG_SENSORS_LTC2990 is not set +# CONFIG_SENSORS_LTC2991 is not set +# CONFIG_SENSORS_LTC2992 is not set +# CONFIG_SENSORS_LTC3815 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4222 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4260 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LTC4282 is not set +# CONFIG_SENSORS_LTC4286 is not set +# CONFIG_SENSORS_LTQ_CPUTEMP is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX127 is not set +# CONFIG_SENSORS_MAX15301 is not set +# CONFIG_SENSORS_MAX16064 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX16601 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX197 is not set +# CONFIG_SENSORS_MAX20730 is not set +# CONFIG_SENSORS_MAX20751 is not set +# CONFIG_SENSORS_MAX31722 is not set +# CONFIG_SENSORS_MAX31730 is not set +# CONFIG_SENSORS_MAX31760 is not set +# CONFIG_SENSORS_MAX31785 is not set +# CONFIG_SENSORS_MAX31790 is not set +# CONFIG_SENSORS_MAX34440 is not set +# CONFIG_SENSORS_MAX6620 is not set +# CONFIG_SENSORS_MAX6621 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6697 is not set +# CONFIG_SENSORS_MAX8688 is not set +# CONFIG_SENSORS_MC34VR500 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_MP2856 is not set +# CONFIG_SENSORS_MP2888 is not set +# CONFIG_SENSORS_MP2891 is not set +# CONFIG_SENSORS_MP2975 is not set +# CONFIG_SENSORS_MP2993 is not set +# CONFIG_SENSORS_MP5023 is not set +# CONFIG_SENSORS_MP5920 is not set +# CONFIG_SENSORS_MP5990 is not set +# CONFIG_SENSORS_MP9941 is not set +# CONFIG_SENSORS_MPQ7932 is not set +# CONFIG_SENSORS_MPQ8785 is not set +# CONFIG_SENSORS_MR75203 is not set +# CONFIG_SENSORS_NCT6683 is not set +# CONFIG_SENSORS_NCT6775 is not set +# CONFIG_SENSORS_NCT6775_I2C is not set +# CONFIG_SENSORS_NCT7802 is not set +# CONFIG_SENSORS_NCT7904 is not set +# CONFIG_SENSORS_NPCM7XX is not set +# CONFIG_SENSORS_NSA320 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +# CONFIG_SENSORS_NZXT_KRAKEN2 is not set +# CONFIG_SENSORS_NZXT_KRAKEN3 is not set +# CONFIG_SENSORS_NZXT_SMART2 is not set +# CONFIG_SENSORS_OCC_P8_I2C is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_PIM4328 is not set +# CONFIG_SENSORS_PLI1209BC is not set +# CONFIG_SENSORS_PM6764TR is not set +# CONFIG_SENSORS_PMBUS is not set +# CONFIG_SENSORS_POWERZ is not set +# CONFIG_SENSORS_POWR1220 is not set +# CONFIG_SENSORS_PT5161L is not set +# CONFIG_SENSORS_PWM_FAN is not set +# CONFIG_SENSORS_PXE1610 is not set +# CONFIG_SENSORS_Q54SJ108A2 is not set +# CONFIG_SENSORS_RM3100_I2C is not set +# CONFIG_SENSORS_RM3100_SPI is not set +# CONFIG_SENSORS_SBRMI is not set +# CONFIG_SENSORS_SBTSI is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SHT3x is not set +# CONFIG_SENSORS_SHT4x is not set +# CONFIG_SENSORS_SHTC1 is not set +# CONFIG_SENSORS_SIS5595 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SPD5118 is not set +# CONFIG_SENSORS_STPDDC60 is not set +# CONFIG_SENSORS_STTS751 is not set +# CONFIG_SENSORS_TC654 is not set +# CONFIG_SENSORS_TC74 is not set +# CONFIG_SENSORS_TDA38640 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP103 is not set +# CONFIG_SENSORS_TMP108 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_TMP464 is not set +# CONFIG_SENSORS_TMP513 is not set +# CONFIG_SENSORS_TPS23861 is not set +# CONFIG_SENSORS_TPS40422 is not set +# CONFIG_SENSORS_TPS53679 is not set +# CONFIG_SENSORS_TPS546D24 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_SENSORS_UCD9000 is not set +# CONFIG_SENSORS_UCD9200 is not set +# CONFIG_SENSORS_VEXPRESS is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_VIA_CPUTEMP is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_VT8231 is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83773G is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_XDP710 is not set +# CONFIG_SENSORS_XDPE122 is not set +# CONFIG_SENSORS_XDPE152 is not set +# CONFIG_SENSORS_XGENE is not set +# CONFIG_SENSORS_ZL6100 is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_16550A_VARIANTS=y +# CONFIG_SERIAL_8250_ACCENT is not set +# CONFIG_SERIAL_8250_ASPEED_VUART is not set +# CONFIG_SERIAL_8250_BOCA is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_CS is not set +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +CONFIG_SERIAL_8250_DMA=y +# CONFIG_SERIAL_8250_DW is not set +# CONFIG_SERIAL_8250_EM is not set +# CONFIG_SERIAL_8250_EXAR is not set +# CONFIG_SERIAL_8250_EXAR_ST16C554 is not set +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_FINTEK is not set +# CONFIG_SERIAL_8250_FOURPORT is not set +# CONFIG_SERIAL_8250_HUB6 is not set +# CONFIG_SERIAL_8250_INGENIC is not set +# CONFIG_SERIAL_8250_LPSS is not set +# CONFIG_SERIAL_8250_MANY_PORTS is not set +# CONFIG_SERIAL_8250_MID is not set +CONFIG_SERIAL_8250_NR_UARTS=2 +# CONFIG_SERIAL_8250_PCI is not set +# CONFIG_SERIAL_8250_PCI1XXXX is not set +# CONFIG_SERIAL_8250_PERICOM is not set +# CONFIG_SERIAL_8250_RSA is not set +# CONFIG_SERIAL_8250_RT288X is not set +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_AMBA_PL010 is not set +# CONFIG_SERIAL_AMBA_PL011 is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_SERIAL_BCM63XX is not set +# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_DEV_BUS is not set +CONFIG_SERIAL_EARLYCON=y +# CONFIG_SERIAL_EARLYCON_SEMIHOST is not set +# CONFIG_SERIAL_FSL_LINFLEXUART is not set +# CONFIG_SERIAL_FSL_LPUART is not set +# CONFIG_SERIAL_GRLIB_GAISLER_APBUART is not set +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +# CONFIG_SERIAL_MULTI_INSTANTIATE is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_SERIAL_OF_PLATFORM is not set +# CONFIG_SERIAL_PCH_UART is not set +# CONFIG_SERIAL_RP2 is not set +# CONFIG_SERIAL_SC16IS7XX is not set +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_SH_SCI is not set +# CONFIG_SERIAL_SIFIVE is not set +# CONFIG_SERIAL_SPRD is not set +# CONFIG_SERIAL_STM32 is not set +# CONFIG_SERIAL_ST_ASC is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_UARTLITE is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIO is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_AMBAKMI is not set +# CONFIG_SERIO_APBPS2 is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_GPIO_PS2 is not set +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_LIBPS2 is not set +# CONFIG_SERIO_PARKBD is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_SUN4I_PS2 is not set +# CONFIG_SERIO_XILINX_XPS_PS2 is not set +# CONFIG_SFC is not set +# CONFIG_SFC_FALCON is not set +# CONFIG_SFC_SIENA is not set +# CONFIG_SFP is not set +# CONFIG_SF_PDMA is not set +# CONFIG_SGETMASK_SYSCALL is not set +# CONFIG_SGI_IP22 is not set +# CONFIG_SGI_IP27 is not set +# CONFIG_SGI_IP28 is not set +# CONFIG_SGI_IP30 is not set +# CONFIG_SGI_IP32 is not set +# CONFIG_SGI_MFD_IOC3 is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_SG_POOL is not set +# CONFIG_SG_SPLIT is not set +# CONFIG_SHADOW_CALL_STACK is not set +CONFIG_SHMEM=y +# CONFIG_SHRINKER_DEBUG is not set +# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set +# CONFIG_SH_ETH is not set +# CONFIG_SH_TIMER_CMT is not set +# CONFIG_SH_TIMER_MTU2 is not set +# CONFIG_SH_TIMER_TMU is not set +# CONFIG_SI1133 is not set +# CONFIG_SI1145 is not set +# CONFIG_SI7005 is not set +# CONFIG_SI7020 is not set +# CONFIG_SIBYTE_BIGSUR is not set +# CONFIG_SIBYTE_CRHONE is not set +# CONFIG_SIBYTE_LITTLESUR is not set +# CONFIG_SIBYTE_RHONE is not set +# CONFIG_SIBYTE_SENTOSA is not set +# CONFIG_SIBYTE_SWARM is not set +CONFIG_SIGNALFD=y +# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set +# CONFIG_SIOX is not set +# CONFIG_SIS190 is not set +# CONFIG_SIS900 is not set +# CONFIG_SKGE is not set +# CONFIG_SKY2 is not set +# CONFIG_SKY2_DEBUG is not set +CONFIG_SLAB_BUCKETS=y +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_SLAB_FREELIST_RANDOM=y +CONFIG_SLAB_MERGE_DEFAULT=y +# CONFIG_SLHC is not set +# CONFIG_SLICOSS is not set +# CONFIG_SLIMBUS is not set +# CONFIG_SLIP is not set +CONFIG_SLUB=y +CONFIG_SLUB_CPU_PARTIAL=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_SLUB_TINY is not set +# CONFIG_SMARTJOYPLUS_FF is not set +# CONFIG_SMB_SERVER is not set +# CONFIG_SMC9194 is not set +# CONFIG_SMC91X is not set +# CONFIG_SMP is not set +# CONFIG_SMSC911X is not set +# CONFIG_SMSC9420 is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_SMS_SDIO_DRV is not set +# CONFIG_SMS_USB_DRV is not set +# CONFIG_SM_CAMCC_8250 is not set +# CONFIG_SM_FTL is not set +# CONFIG_SM_GCC_6115 is not set +# CONFIG_SM_GCC_6125 is not set +# CONFIG_SM_GCC_6350 is not set +# CONFIG_SM_GCC_6375 is not set +# CONFIG_SM_GCC_8350 is not set +# CONFIG_SND is not set +# CONFIG_SND_AC97_POWER_SAVE is not set +# CONFIG_SND_AD1816A is not set +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_AD1889 is not set +# CONFIG_SND_ADLIB is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ALOOP is not set +# CONFIG_SND_ALS100 is not set +# CONFIG_SND_ALS300 is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_AMD_ACP_CONFIG is not set +# CONFIG_SND_ARM is not set +# CONFIG_SND_ASIHPI is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_ATMEL_AC97C is not set +# CONFIG_SND_ATMEL_SOC is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AUDIO_GRAPH_CARD is not set +# CONFIG_SND_AUDIO_GRAPH_CARD2 is not set +# CONFIG_SND_AW2 is not set +# CONFIG_SND_AZT2320 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BCD2000 is not set +# CONFIG_SND_BCM2835 is not set +# CONFIG_SND_BCM63XX_I2S_WHISTLER is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS5530 is not set +# CONFIG_SND_CS5535AUDIO is not set +# CONFIG_SND_CTL_FAST_LOOKUP is not set +# CONFIG_SND_CTL_INPUT_VALIDATION is not set +# CONFIG_SND_CTXFI is not set +# CONFIG_SND_DARLA20 is not set +# CONFIG_SND_DARLA24 is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_DESIGNWARE_I2S is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +# CONFIG_SND_ECHO3G is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_EMU10K1X is not set +# CONFIG_SND_EMU10K1_SEQ is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_FIREWIRE is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_GINA20 is not set +# CONFIG_SND_GINA24 is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_HDA_CODEC_CS8409 is not set +# CONFIG_SND_HDA_CODEC_SENARYTECH is not set +# CONFIG_SND_HDA_CTL_DEV_ID is not set +# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM is not set +CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0 +CONFIG_SND_HDA_PREALLOC_SIZE=64 +# CONFIG_SND_HDA_SCODEC_CS35L41_I2C is not set +# CONFIG_SND_HDA_SCODEC_CS35L41_SPI is not set +# CONFIG_SND_HDA_SCODEC_CS35L56_I2C is not set +# CONFIG_SND_HDA_SCODEC_CS35L56_SPI is not set +# CONFIG_SND_HDA_SCODEC_TAS2781_I2C is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_HWDEP is not set +# CONFIG_SND_I2S_HI6210_I2S is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INDIGO is not set +# CONFIG_SND_INDIGODJ is not set +# CONFIG_SND_INDIGODJX is not set +# CONFIG_SND_INDIGOIO is not set +# CONFIG_SND_INDIGOIOX is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_ISA is not set +# CONFIG_SND_JZ4740_SOC_I2S is not set +# CONFIG_SND_KIRKWOOD_SOC is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_LAYLA20 is not set +# CONFIG_SND_LAYLA24 is not set +# CONFIG_SND_LOLA is not set +# CONFIG_SND_LX6464ES is not set +# CONFIG_SND_MAESTRO3 is not set +CONFIG_SND_MAX_CARDS=16 +# CONFIG_SND_MIA is not set +# CONFIG_SND_MIPS is not set +# CONFIG_SND_MIRO is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_MONA is not set +# CONFIG_SND_MPC52xx_SOC_EFIKA is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_MTS64 is not set +# CONFIG_SND_MXS_SOC is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +CONFIG_SND_OSSEMUL=y +# CONFIG_SND_OXYGEN is not set +CONFIG_SND_PCI=y +# CONFIG_SND_PCM is not set +# CONFIG_SND_PCMCIA is not set +# CONFIG_SND_PCMTEST is not set +# CONFIG_SND_PCM_OSS is not set +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_PCM_TIMER is not set +# CONFIG_SND_PCM_XRUN_DEBUG is not set +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_PDAUDIOCF is not set +# CONFIG_SND_PORTMAN2X4 is not set +# CONFIG_SND_POWERPC_SOC is not set +# CONFIG_SND_PPC is not set +CONFIG_SND_PROC_FS=y +# CONFIG_SND_RAWMIDI is not set +# CONFIG_SND_RIPTIDE is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_SE6X is not set +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_SEQ_UMP is not set +# CONFIG_SND_SERIAL_GENERIC is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_SIMPLE_CARD is not set +# CONFIG_SND_SIS7019 is not set +# CONFIG_SND_SOC is not set +# CONFIG_SND_SOC_AC97_CODEC is not set +# CONFIG_SND_SOC_AD193X_I2C is not set +# CONFIG_SND_SOC_AD193X_SPI is not set +# CONFIG_SND_SOC_ADAU1372_I2C is not set +# CONFIG_SND_SOC_ADAU1372_SPI is not set +# CONFIG_SND_SOC_ADAU1701 is not set +# CONFIG_SND_SOC_ADAU1761_I2C is not set +# CONFIG_SND_SOC_ADAU1761_SPI is not set +# CONFIG_SND_SOC_ADAU7002 is not set +# CONFIG_SND_SOC_ADAU7118_HW is not set +# CONFIG_SND_SOC_ADAU7118_I2C is not set +# CONFIG_SND_SOC_ADI is not set +# CONFIG_SND_SOC_AK4104 is not set +# CONFIG_SND_SOC_AK4118 is not set +# CONFIG_SND_SOC_AK4375 is not set +# CONFIG_SND_SOC_AK4458 is not set +# CONFIG_SND_SOC_AK4554 is not set +# CONFIG_SND_SOC_AK4613 is not set +# CONFIG_SND_SOC_AK4619 is not set +# CONFIG_SND_SOC_AK4642 is not set +# CONFIG_SND_SOC_AK5386 is not set +# CONFIG_SND_SOC_AK5558 is not set +# CONFIG_SND_SOC_ALC5623 is not set +# CONFIG_SND_SOC_AMD_ACP is not set +# CONFIG_SND_SOC_AMD_ACP3x is not set +# CONFIG_SND_SOC_AMD_ACP5x is not set +# CONFIG_SND_SOC_AMD_ACP6x is not set +# CONFIG_SND_SOC_AMD_ACP_COMMON is not set +# CONFIG_SND_SOC_AMD_PS is not set +# CONFIG_SND_SOC_AMD_RENOIR is not set +# CONFIG_SND_SOC_AMD_RPL_ACP6x is not set +# CONFIG_SND_SOC_AU1XAUDIO is not set +# CONFIG_SND_SOC_AU1XPSC is not set +# CONFIG_SND_SOC_AUDIO_IIO_AUX is not set +# CONFIG_SND_SOC_AW8738 is not set +# CONFIG_SND_SOC_AW87390 is not set +# CONFIG_SND_SOC_AW88261 is not set +# CONFIG_SND_SOC_AW88395 is not set +# CONFIG_SND_SOC_AW88399 is not set +# CONFIG_SND_SOC_BD28623 is not set +# CONFIG_SND_SOC_BT_SCO is not set +# CONFIG_SND_SOC_CHV3_CODEC is not set +# CONFIG_SND_SOC_CHV3_I2S is not set +# CONFIG_SND_SOC_CS35L32 is not set +# CONFIG_SND_SOC_CS35L33 is not set +# CONFIG_SND_SOC_CS35L34 is not set +# CONFIG_SND_SOC_CS35L35 is not set +# CONFIG_SND_SOC_CS35L36 is not set +# CONFIG_SND_SOC_CS35L41_I2C is not set +# CONFIG_SND_SOC_CS35L41_SPI is not set +# CONFIG_SND_SOC_CS35L45_I2C is not set +# CONFIG_SND_SOC_CS35L45_SPI is not set +# CONFIG_SND_SOC_CS35L56_I2C is not set +# CONFIG_SND_SOC_CS35L56_SPI is not set +# CONFIG_SND_SOC_CS4234 is not set +# CONFIG_SND_SOC_CS4265 is not set +# CONFIG_SND_SOC_CS4270 is not set +# CONFIG_SND_SOC_CS4271 is not set +# CONFIG_SND_SOC_CS4271_I2C is not set +# CONFIG_SND_SOC_CS4271_SPI is not set +# CONFIG_SND_SOC_CS42L42 is not set +# CONFIG_SND_SOC_CS42L51_I2C is not set +# CONFIG_SND_SOC_CS42L52 is not set +# CONFIG_SND_SOC_CS42L56 is not set +# CONFIG_SND_SOC_CS42L73 is not set +# CONFIG_SND_SOC_CS42L83 is not set +# CONFIG_SND_SOC_CS42XX8_I2C is not set +# CONFIG_SND_SOC_CS43130 is not set +# CONFIG_SND_SOC_CS4341 is not set +# CONFIG_SND_SOC_CS4349 is not set +# CONFIG_SND_SOC_CS530X_I2C is not set +# CONFIG_SND_SOC_CS53L30 is not set +# CONFIG_SND_SOC_CX2072X is not set +# CONFIG_SND_SOC_DA7213 is not set +# CONFIG_SND_SOC_DMIC is not set +# CONFIG_SND_SOC_ES7134 is not set +# CONFIG_SND_SOC_ES7241 is not set +# CONFIG_SND_SOC_ES8311 is not set +# CONFIG_SND_SOC_ES8316 is not set +# CONFIG_SND_SOC_ES8326 is not set +# CONFIG_SND_SOC_ES8328 is not set +# CONFIG_SND_SOC_ES8328_I2C is not set +# CONFIG_SND_SOC_ES8328_SPI is not set +# CONFIG_SND_SOC_EUKREA_TLV320 is not set +# CONFIG_SND_SOC_FSL_ASOC_CARD is not set +# CONFIG_SND_SOC_FSL_ASRC is not set +# CONFIG_SND_SOC_FSL_AUD2HTX is not set +# CONFIG_SND_SOC_FSL_AUDMIX is not set +# CONFIG_SND_SOC_FSL_ESAI is not set +# CONFIG_SND_SOC_FSL_MICFIL is not set +# CONFIG_SND_SOC_FSL_RPMSG is not set +# CONFIG_SND_SOC_FSL_SAI is not set +# CONFIG_SND_SOC_FSL_SPDIF is not set +# CONFIG_SND_SOC_FSL_SSI is not set +# CONFIG_SND_SOC_FSL_XCVR is not set +# CONFIG_SND_SOC_GTM601 is not set +# CONFIG_SND_SOC_HDA is not set +# CONFIG_SND_SOC_ICS43432 is not set +# CONFIG_SND_SOC_IDT821034 is not set +# CONFIG_SND_SOC_IMG is not set +# CONFIG_SND_SOC_IMX_AUDMIX is not set +# CONFIG_SND_SOC_IMX_AUDMUX is not set +# CONFIG_SND_SOC_IMX_CARD is not set +# CONFIG_SND_SOC_IMX_ES8328 is not set +# CONFIG_SND_SOC_IMX_HDMI is not set +# CONFIG_SND_SOC_IMX_RPMSG is not set +# CONFIG_SND_SOC_INNO_RK3036 is not set +# CONFIG_SND_SOC_INTEL_AVS is not set +# CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH is not set +# CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH is not set +# CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH is not set +# CONFIG_SND_SOC_INTEL_CATPT is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH is not set +# CONFIG_SND_SOC_INTEL_HASWELL is not set +# CONFIG_SND_SOC_INTEL_KEEMBAY is not set +# CONFIG_SND_SOC_INTEL_SST is not set +CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y +# CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES is not set +# CONFIG_SND_SOC_JZ4725B_CODEC is not set +# CONFIG_SND_SOC_JZ4740_CODEC is not set +# CONFIG_SND_SOC_JZ4770_CODEC is not set +# CONFIG_SND_SOC_LOONGSON_CARD is not set +# CONFIG_SND_SOC_LOONGSON_I2S_PCI is not set +# CONFIG_SND_SOC_LPASS_RX_MACRO is not set +# CONFIG_SND_SOC_LPASS_TX_MACRO is not set +# CONFIG_SND_SOC_LPASS_VA_MACRO is not set +# CONFIG_SND_SOC_LPASS_WSA_MACRO is not set +# CONFIG_SND_SOC_MAX9759 is not set +# CONFIG_SND_SOC_MAX98088 is not set +# CONFIG_SND_SOC_MAX98090 is not set +# CONFIG_SND_SOC_MAX98357A is not set +# CONFIG_SND_SOC_MAX98373 is not set +# CONFIG_SND_SOC_MAX98373_I2C is not set +# CONFIG_SND_SOC_MAX98388 is not set +# CONFIG_SND_SOC_MAX98390 is not set +# CONFIG_SND_SOC_MAX98396 is not set +# CONFIG_SND_SOC_MAX98504 is not set +# CONFIG_SND_SOC_MAX98520 is not set +# CONFIG_SND_SOC_MAX9860 is not set +# CONFIG_SND_SOC_MAX9867 is not set +# CONFIG_SND_SOC_MAX98927 is not set +# CONFIG_SND_SOC_MEDIATEK is not set +# CONFIG_SND_SOC_MPC5200_AC97 is not set +# CONFIG_SND_SOC_MPC5200_I2S is not set +# CONFIG_SND_SOC_MSM8916_WCD_ANALOG is not set +# CONFIG_SND_SOC_MSM8916_WCD_DIGITAL is not set +# CONFIG_SND_SOC_MT2701 is not set +# CONFIG_SND_SOC_MT6351 is not set +# CONFIG_SND_SOC_MT6357 is not set +# CONFIG_SND_SOC_MT6358 is not set +# CONFIG_SND_SOC_MT6359 is not set +# CONFIG_SND_SOC_MT6359_ACCDET is not set +# CONFIG_SND_SOC_MT6660 is not set +# CONFIG_SND_SOC_MT6797 is not set +# CONFIG_SND_SOC_MT8173 is not set +# CONFIG_SND_SOC_MT8183 is not set +# CONFIG_SND_SOC_MT8186 is not set +# CONFIG_SND_SOC_MT8188 is not set +# CONFIG_SND_SOC_MT8192 is not set +# CONFIG_SND_SOC_MT8195 is not set +# CONFIG_SND_SOC_MT8365 is not set +# CONFIG_SND_SOC_MTK_BTCVSD is not set +# CONFIG_SND_SOC_NAU8315 is not set +# CONFIG_SND_SOC_NAU8325 is not set +# CONFIG_SND_SOC_NAU8540 is not set +# CONFIG_SND_SOC_NAU8810 is not set +# CONFIG_SND_SOC_NAU8821 is not set +# CONFIG_SND_SOC_NAU8822 is not set +# CONFIG_SND_SOC_NAU8824 is not set +# CONFIG_SND_SOC_PCM1681 is not set +# CONFIG_SND_SOC_PCM1789_I2C is not set +# CONFIG_SND_SOC_PCM179X_I2C is not set +# CONFIG_SND_SOC_PCM179X_SPI is not set +# CONFIG_SND_SOC_PCM186X_I2C is not set +# CONFIG_SND_SOC_PCM186X_SPI is not set +# CONFIG_SND_SOC_PCM3060_I2C is not set +# CONFIG_SND_SOC_PCM3060_SPI is not set +# CONFIG_SND_SOC_PCM3168A_I2C is not set +# CONFIG_SND_SOC_PCM3168A_SPI is not set +# CONFIG_SND_SOC_PCM5102A is not set +# CONFIG_SND_SOC_PCM512x_I2C is not set +# CONFIG_SND_SOC_PCM512x_SPI is not set +# CONFIG_SND_SOC_PCM6240 is not set +# CONFIG_SND_SOC_PEB2466 is not set +# CONFIG_SND_SOC_QCOM is not set +# CONFIG_SND_SOC_RK3308 is not set +# CONFIG_SND_SOC_RK3328 is not set +# CONFIG_SND_SOC_RK817 is not set +# CONFIG_SND_SOC_ROCKCHIP is not set +# CONFIG_SND_SOC_RT5616 is not set +# CONFIG_SND_SOC_RT5631 is not set +# CONFIG_SND_SOC_RT5640 is not set +# CONFIG_SND_SOC_RT5659 is not set +# CONFIG_SND_SOC_RT5677_SPI is not set +# CONFIG_SND_SOC_RT9120 is not set +# CONFIG_SND_SOC_RTQ9128 is not set +# CONFIG_SND_SOC_SGTL5000 is not set +# CONFIG_SND_SOC_SIMPLE_AMPLIFIER is not set +# CONFIG_SND_SOC_SIMPLE_MUX is not set +# CONFIG_SND_SOC_SMA1303 is not set +# CONFIG_SND_SOC_SOF_TOPLEVEL is not set +# CONFIG_SND_SOC_SPDIF is not set +# CONFIG_SND_SOC_SRC4XXX_I2C is not set +# CONFIG_SND_SOC_SSM2305 is not set +# CONFIG_SND_SOC_SSM2518 is not set +# CONFIG_SND_SOC_SSM2602_I2C is not set +# CONFIG_SND_SOC_SSM2602_SPI is not set +# CONFIG_SND_SOC_SSM3515 is not set +# CONFIG_SND_SOC_SSM4567 is not set +# CONFIG_SND_SOC_STA32X is not set +# CONFIG_SND_SOC_STA350 is not set +# CONFIG_SND_SOC_STI_SAS is not set +# CONFIG_SND_SOC_TAS2552 is not set +# CONFIG_SND_SOC_TAS2562 is not set +# CONFIG_SND_SOC_TAS2764 is not set +# CONFIG_SND_SOC_TAS2770 is not set +# CONFIG_SND_SOC_TAS2780 is not set +# CONFIG_SND_SOC_TAS2781_I2C is not set +# CONFIG_SND_SOC_TAS5086 is not set +# CONFIG_SND_SOC_TAS571X is not set +# CONFIG_SND_SOC_TAS5720 is not set +# CONFIG_SND_SOC_TAS5805M is not set +# CONFIG_SND_SOC_TAS6424 is not set +# CONFIG_SND_SOC_TDA7419 is not set +# CONFIG_SND_SOC_TFA9879 is not set +# CONFIG_SND_SOC_TFA989X is not set +# CONFIG_SND_SOC_TLV320ADC3XXX is not set +# CONFIG_SND_SOC_TLV320ADCX140 is not set +# CONFIG_SND_SOC_TLV320AIC23_I2C is not set +# CONFIG_SND_SOC_TLV320AIC23_SPI is not set +# CONFIG_SND_SOC_TLV320AIC31XX is not set +# CONFIG_SND_SOC_TLV320AIC32X4_I2C is not set +# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set +# CONFIG_SND_SOC_TLV320AIC3X is not set +# CONFIG_SND_SOC_TLV320AIC3X_I2C is not set +# CONFIG_SND_SOC_TLV320AIC3X_SPI is not set +# CONFIG_SND_SOC_TPA6130A2 is not set +# CONFIG_SND_SOC_TS3A227E is not set +# CONFIG_SND_SOC_TSCS42XX is not set +# CONFIG_SND_SOC_TSCS454 is not set +# CONFIG_SND_SOC_UDA1334 is not set +# CONFIG_SND_SOC_WM8510 is not set +# CONFIG_SND_SOC_WM8523 is not set +# CONFIG_SND_SOC_WM8524 is not set +# CONFIG_SND_SOC_WM8580 is not set +# CONFIG_SND_SOC_WM8711 is not set +# CONFIG_SND_SOC_WM8728 is not set +# CONFIG_SND_SOC_WM8731 is not set +# CONFIG_SND_SOC_WM8731_I2C is not set +# CONFIG_SND_SOC_WM8731_SPI is not set +# CONFIG_SND_SOC_WM8737 is not set +# CONFIG_SND_SOC_WM8741 is not set +# CONFIG_SND_SOC_WM8750 is not set +# CONFIG_SND_SOC_WM8753 is not set +# CONFIG_SND_SOC_WM8770 is not set +# CONFIG_SND_SOC_WM8776 is not set +# CONFIG_SND_SOC_WM8782 is not set +# CONFIG_SND_SOC_WM8804_I2C is not set +# CONFIG_SND_SOC_WM8804_SPI is not set +# CONFIG_SND_SOC_WM8903 is not set +# CONFIG_SND_SOC_WM8904 is not set +# CONFIG_SND_SOC_WM8940 is not set +# CONFIG_SND_SOC_WM8960 is not set +# CONFIG_SND_SOC_WM8961 is not set +# CONFIG_SND_SOC_WM8962 is not set +# CONFIG_SND_SOC_WM8974 is not set +# CONFIG_SND_SOC_WM8978 is not set +# CONFIG_SND_SOC_WM8985 is not set +# CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER is not set +# CONFIG_SND_SOC_XILINX_I2S is not set +# CONFIG_SND_SOC_XILINX_SPDIF is not set +# CONFIG_SND_SOC_XTFPGA_I2S is not set +# CONFIG_SND_SOC_ZL38060 is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_SPI is not set +# CONFIG_SND_SSCAPE is not set +# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI is not set +# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set +# CONFIG_SND_SUN4I_CODEC is not set +# CONFIG_SND_SUPPORT_OLD_API is not set +# CONFIG_SND_TEST_COMPONENT is not set +# CONFIG_SND_TIMER is not set +# CONFIG_SND_TRIDENT is not set +CONFIG_SND_USB=y +# CONFIG_SND_USB_6FIRE is not set +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_AUDIO_MIDI_V2 is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_HIFACE is not set +# CONFIG_SND_USB_POD is not set +# CONFIG_SND_USB_PODHD is not set +# CONFIG_SND_USB_TONEPORT is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_US122L is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_USB_VARIAX is not set +# CONFIG_SND_UTIMER is not set +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VIA82XX_MODEM is not set +# CONFIG_SND_VIRTIO is not set +# CONFIG_SND_VIRTUOSO is not set +# CONFIG_SND_VX222 is not set +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_WAVEFRONT is not set +CONFIG_SND_X86=y +# CONFIG_SND_XEN_FRONTEND is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SNI_RM is not set +# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set +# CONFIG_SOCK_CGROUP_DATA is not set +# CONFIG_SOC_AM33XX is not set +# CONFIG_SOC_AM43XX is not set +# CONFIG_SOC_BRCMSTB is not set +# CONFIG_SOC_DRA7XX is not set +# CONFIG_SOC_HAS_OMAP2_SDRC is not set +# CONFIG_SOC_OMAP5 is not set +# CONFIG_SOC_TI is not set +# CONFIG_SOFTLOCKUP_DETECTOR is not set +# CONFIG_SOFTLOCKUP_DETECTOR_INTR_STORM is not set +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_SONYPI is not set +# CONFIG_SONY_LAPTOP is not set +# CONFIG_SOUND is not set +# CONFIG_SOUNDWIRE is not set +# CONFIG_SOUND_OSS_CORE is not set +# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set +# CONFIG_SP5100_TCO is not set +# CONFIG_SPARSEMEM_MANUAL is not set +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +# CONFIG_SPARSE_IRQ is not set +# CONFIG_SPEAKUP is not set +# CONFIG_SPI is not set +# CONFIG_SPINLOCK_TEST is not set +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_AMD is not set +# CONFIG_SPI_AU1550 is not set +# CONFIG_SPI_AX88796C is not set +# CONFIG_SPI_AXI_SPI_ENGINE is not set +# CONFIG_SPI_BCM2835 is not set +# CONFIG_SPI_BCM63XX_HSSPI is not set +# CONFIG_SPI_BCMBCA_HSSPI is not set +# CONFIG_SPI_BCM_QSPI is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_BUTTERFLY is not set +# CONFIG_SPI_CADENCE is not set +# CONFIG_SPI_CADENCE_QUADSPI is not set +# CONFIG_SPI_CADENCE_XSPI is not set +# CONFIG_SPI_CH341 is not set +# CONFIG_SPI_DEBUG is not set +# CONFIG_SPI_DESIGNWARE is not set +# CONFIG_SPI_DW_BT1 is not set +# CONFIG_SPI_DW_BT1_DIRMAP is not set +# CONFIG_SPI_DW_DMA is not set +# CONFIG_SPI_DW_MMIO is not set +# CONFIG_SPI_DW_PCI is not set +# CONFIG_SPI_FSL_DSPI is not set +# CONFIG_SPI_FSL_ESPI is not set +# CONFIG_SPI_FSL_SPI is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_IMG_SPFI is not set +# CONFIG_SPI_LANTIQ_SSC is not set +# CONFIG_SPI_LM70_LLP is not set +# CONFIG_SPI_LOOPBACK_TEST is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_SPI_MEM is not set +# CONFIG_SPI_MICROCHIP_CORE is not set +# CONFIG_SPI_MICROCHIP_CORE_QSPI is not set +# CONFIG_SPI_MPC52xx is not set +# CONFIG_SPI_MPC52xx_PSC is not set +# CONFIG_SPI_MUX is not set +# CONFIG_SPI_MXIC is not set +# CONFIG_SPI_NXP_FLEXSPI is not set +# CONFIG_SPI_OCTEON is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_ORION is not set +# CONFIG_SPI_PCI1XXXX is not set +# CONFIG_SPI_PL022 is not set +# CONFIG_SPI_PPC4xx is not set +# CONFIG_SPI_PXA2XX is not set +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_QCOM_QSPI is not set +# CONFIG_SPI_ROCKCHIP is not set +# CONFIG_SPI_S3C64XX is not set +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_SIFIVE is not set +# CONFIG_SPI_SLAVE is not set +# CONFIG_SPI_SN_F_OSPI is not set +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_THUNDERX is not set +# CONFIG_SPI_TI_QSPI is not set +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_SPI_TOPCLIFF_PCH is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_ZYNQMP_GQSPI is not set +# CONFIG_SPMI is not set +# CONFIG_SPS30 is not set +# CONFIG_SPS30_I2C is not set +# CONFIG_SPS30_SERIAL is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_CHOICE_DECOMP_BY_MOUNT is not set +# CONFIG_SQUASHFS_COMPILE_DECOMP_MULTI is not set +CONFIG_SQUASHFS_COMPILE_DECOMP_MULTI_PERCPU=y +# CONFIG_SQUASHFS_COMPILE_DECOMP_SINGLE is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y +CONFIG_SQUASHFS_EMBEDDED=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_SQUASHFS_LZ4 is not set +# CONFIG_SQUASHFS_LZO is not set +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_ZLIB is not set +# CONFIG_SQUASHFS_ZSTD is not set +# CONFIG_SRAM is not set +# CONFIG_SRF04 is not set +# CONFIG_SRF08 is not set +# CONFIG_SSB is not set +# CONFIG_SSB_DRIVER_GPIO is not set +# CONFIG_SSB_HOST_SOC is not set +# CONFIG_SSB_PCMCIAHOST is not set +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB_SDIOHOST is not set +# CONFIG_SSFDC is not set +# CONFIG_SSIF_IPMI_BMC is not set +CONFIG_STACKDEPOT_MAX_FRAMES=64 +# CONFIG_STACKPROTECTOR is not set +# CONFIG_STACKPROTECTOR_PER_TASK is not set +# CONFIG_STACKPROTECTOR_STRONG is not set +# CONFIG_STACKTRACE is not set +# CONFIG_STACKTRACE_BUILD_ID is not set +CONFIG_STACKTRACE_SUPPORT=y +# CONFIG_STACK_TRACER is not set +# CONFIG_STACK_VALIDATION is not set +CONFIG_STAGING=y +# CONFIG_STAGING_MEDIA is not set +CONFIG_STANDALONE=y +# CONFIG_STATIC_KEYS_SELFTEST is not set +# CONFIG_STATIC_USERMODEHELPER is not set +# CONFIG_STE10XP is not set +# CONFIG_STK3310 is not set +# CONFIG_STK8312 is not set +# CONFIG_STK8BA50 is not set +# CONFIG_STM is not set +# CONFIG_STMMAC_ETH is not set +# CONFIG_STMMAC_PCI is not set +# CONFIG_STMMAC_PLATFORM is not set +# CONFIG_STMMAC_SELFTESTS is not set +# CONFIG_STMPE_ADC is not set +# CONFIG_STM_DUMMY is not set +# CONFIG_STM_SOURCE_CONSOLE is not set +CONFIG_STP=y +# CONFIG_STREAM_PARSER is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_STRICT_KERNEL_RWX=y +CONFIG_STRICT_MODULE_RWX=y +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_STX104 is not set +# CONFIG_ST_UVIS25 is not set +# CONFIG_SUN4I_GPADC is not set +# CONFIG_SUN50I_DE2_BUS is not set +# CONFIG_SUN50I_ERRATUM_UNKNOWN1 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_SUNRPC is not set +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_SUNRPC_GSS is not set +# CONFIG_SUNXI_SRAM is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_SURFACE_PLATFORMS is not set +# CONFIG_SUSPEND is not set +# CONFIG_SUSPEND_SKIP_SYNC is not set +CONFIG_SWAP=y +# CONFIG_SWCONFIG is not set +# CONFIG_SWCONFIG_B53 is not set +# CONFIG_SWCONFIG_B53_MMAP_DRIVER is not set +# CONFIG_SWCONFIG_B53_SPI_DRIVER is not set +# CONFIG_SWCONFIG_B53_SRAB_DRIVER is not set +# CONFIG_SWCONFIG_LEDS is not set +# CONFIG_SWIOTLB is not set +# CONFIG_SWIOTLB_DYNAMIC is not set +# CONFIG_SW_SYNC is not set +# CONFIG_SX9310 is not set +# CONFIG_SX9324 is not set +# CONFIG_SX9360 is not set +# CONFIG_SX9500 is not set +# CONFIG_SXGBE_ETH is not set +CONFIG_SYMBOLIC_ERRNAME=y +# CONFIG_SYNC_FILE is not set +# CONFIG_SYNTH_EVENTS is not set +# CONFIG_SYNTH_EVENT_GEN_TEST is not set +CONFIG_SYN_COOKIES=y +# CONFIG_SYSCON_REBOOT_MODE is not set +CONFIG_SYSCTL=y +CONFIG_SYSFS=y +# CONFIG_SYSFS_SYSCALL is not set +# CONFIG_SYSTEMPORT is not set +# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set +# CONFIG_SYSTEM_DATA_VERIFICATION is not set +# CONFIG_SYSTEM_TRUSTED_KEYRING is not set +CONFIG_SYSTEM_TRUSTED_KEYS="" +# CONFIG_SYSV68_PARTITION is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_T5403 is not set +# CONFIG_TARGET_CORE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_TASKS_RCU is not set +# CONFIG_TASK_XACCT is not set +# CONFIG_TC35815 is not set +# CONFIG_TCG_ATMEL is not set +# CONFIG_TCG_CRB is not set +# CONFIG_TCG_FTPM_TEE is not set +# CONFIG_TCG_INFINEON is not set +# CONFIG_TCG_NSC is not set +# CONFIG_TCG_TIS is not set +# CONFIG_TCG_TIS_I2C is not set +# CONFIG_TCG_TIS_I2C_ATMEL is not set +# CONFIG_TCG_TIS_I2C_CR50 is not set +# CONFIG_TCG_TIS_I2C_INFINEON is not set +# CONFIG_TCG_TIS_I2C_NUVOTON is not set +# CONFIG_TCG_TIS_SPI is not set +# CONFIG_TCG_TIS_ST33ZP24_I2C is not set +# CONFIG_TCG_TIS_ST33ZP24_SPI is not set +# CONFIG_TCG_TPM is not set +# CONFIG_TCG_TPM2_HMAC is not set +# CONFIG_TCG_VTPM_PROXY is not set +# CONFIG_TCG_XEN is not set +# CONFIG_TCIC is not set +# CONFIG_TCP_AO is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BBR is not set +# CONFIG_TCP_CONG_BIC is not set +# CONFIG_TCP_CONG_CDG is not set +CONFIG_TCP_CONG_CUBIC=y +# CONFIG_TCP_CONG_DCTCP is not set +# CONFIG_TCP_CONG_HSTCP is not set +# CONFIG_TCP_CONG_HTCP is not set +# CONFIG_TCP_CONG_HYBLA is not set +# CONFIG_TCP_CONG_ILLINOIS is not set +# CONFIG_TCP_CONG_LP is not set +# CONFIG_TCP_CONG_NV is not set +# CONFIG_TCP_CONG_SCALABLE is not set +# CONFIG_TCP_CONG_VEGAS is not set +# CONFIG_TCP_CONG_VENO is not set +# CONFIG_TCP_CONG_WESTWOOD is not set +# CONFIG_TCP_CONG_YEAH is not set +# CONFIG_TCP_MD5SIG is not set +# CONFIG_TCS3414 is not set +# CONFIG_TCS3472 is not set +# CONFIG_TEE is not set +# CONFIG_TEGRA_AHB is not set +# CONFIG_TEGRA_HOST1X is not set +# CONFIG_TEHUTI is not set +# CONFIG_TEHUTI_TN40 is not set +# CONFIG_TERANETICS_PHY is not set +# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set +# CONFIG_TEST_BITMAP is not set +# CONFIG_TEST_BITOPS is not set +# CONFIG_TEST_BLACKHOLE_DEV is not set +# CONFIG_TEST_BPF is not set +# CONFIG_TEST_CLOCKSOURCE_WATCHDOG is not set +# CONFIG_TEST_DEBUG_VIRTUAL is not set +# CONFIG_TEST_DHRY is not set +# CONFIG_TEST_DIV64 is not set +# CONFIG_TEST_DYNAMIC_DEBUG is not set +# CONFIG_TEST_FIRMWARE is not set +# CONFIG_TEST_FPU is not set +# CONFIG_TEST_FREE_PAGES is not set +# CONFIG_TEST_HEXDUMP is not set +# CONFIG_TEST_IDA is not set +# CONFIG_TEST_KMOD is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_TEST_LKM is not set +# CONFIG_TEST_LOCKUP is not set +# CONFIG_TEST_MAPLE_TREE is not set +# CONFIG_TEST_MEMCAT_P is not set +# CONFIG_TEST_MEMINIT is not set +# CONFIG_TEST_MIN_HEAP is not set +# CONFIG_TEST_MULDIV64 is not set +# CONFIG_TEST_OBJPOOL is not set +# CONFIG_TEST_POWER is not set +# CONFIG_TEST_PRINTF is not set +# CONFIG_TEST_REF_TRACKER is not set +# CONFIG_TEST_RHASHTABLE is not set +# CONFIG_TEST_SCANF is not set +# CONFIG_TEST_SORT is not set +# CONFIG_TEST_STATIC_KEYS is not set +# CONFIG_TEST_SYSCTL is not set +# CONFIG_TEST_UBSAN is not set +# CONFIG_TEST_UDELAY is not set +# CONFIG_TEST_UUID is not set +# CONFIG_TEST_VMALLOC is not set +# CONFIG_TEST_XARRAY is not set +CONFIG_TEXTSEARCH=y +# CONFIG_TEXTSEARCH_BM is not set +# CONFIG_TEXTSEARCH_FSM is not set +# CONFIG_TEXTSEARCH_KMP is not set +# CONFIG_THERMAL is not set +# CONFIG_THERMAL_CORE_TESTING is not set +# CONFIG_THERMAL_DEBUGFS is not set +# CONFIG_THERMAL_DEFAULT_GOV_BANG_BANG is not set +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_EMULATION is not set +# CONFIG_THERMAL_GOV_BANG_BANG is not set +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_GOV_USER_SPACE is not set +# CONFIG_THERMAL_HWMON is not set +# CONFIG_THERMAL_MMIO is not set +# CONFIG_THERMAL_NETLINK is not set +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_THINKPAD_ACPI is not set +# CONFIG_THINKPAD_LMI is not set +# CONFIG_THRUSTMASTER_FF is not set +# CONFIG_THUMB2_KERNEL is not set +# CONFIG_THUNDER_NIC_BGX is not set +# CONFIG_THUNDER_NIC_PF is not set +# CONFIG_THUNDER_NIC_RGX is not set +# CONFIG_THUNDER_NIC_VF is not set +# CONFIG_TICK_CPU_ACCOUNTING is not set +CONFIG_TICK_ONESHOT=y +# CONFIG_TIFM_CORE is not set +# CONFIG_TIGON3 is not set +# CONFIG_TIMB_DMA is not set +CONFIG_TIMERFD=y +# CONFIG_TIMERLAT_TRACER is not set +# CONFIG_TIME_NS is not set +# CONFIG_TINYDRM_HX8357D is not set +# CONFIG_TINYDRM_ILI9163 is not set +# CONFIG_TINYDRM_ILI9225 is not set +# CONFIG_TINYDRM_ILI9341 is not set +# CONFIG_TINYDRM_ILI9486 is not set +# CONFIG_TINYDRM_MI0283QT is not set +# CONFIG_TINYDRM_REPAPER is not set +# CONFIG_TINYDRM_ST7586 is not set +# CONFIG_TINYDRM_ST7735R is not set +CONFIG_TINY_RCU=y +# CONFIG_TIPC is not set +# CONFIG_TI_ADC081C is not set +# CONFIG_TI_ADC0832 is not set +# CONFIG_TI_ADC084S021 is not set +# CONFIG_TI_ADC108S102 is not set +# CONFIG_TI_ADC12138 is not set +# CONFIG_TI_ADC128S052 is not set +# CONFIG_TI_ADC161S626 is not set +# CONFIG_TI_ADS1015 is not set +# CONFIG_TI_ADS1100 is not set +# CONFIG_TI_ADS1119 is not set +# CONFIG_TI_ADS124S08 is not set +# CONFIG_TI_ADS1298 is not set +# CONFIG_TI_ADS131E08 is not set +# CONFIG_TI_ADS7924 is not set +# CONFIG_TI_ADS7950 is not set +# CONFIG_TI_ADS8344 is not set +# CONFIG_TI_ADS8688 is not set +# CONFIG_TI_AM335X_ADC is not set +# CONFIG_TI_CPSW is not set +# CONFIG_TI_CPSW_PHY_SEL is not set +# CONFIG_TI_CPTS is not set +# CONFIG_TI_DAC082S085 is not set +# CONFIG_TI_DAC5571 is not set +# CONFIG_TI_DAC7311 is not set +# CONFIG_TI_DAC7612 is not set +# CONFIG_TI_DAVINCI_MDIO is not set +# CONFIG_TI_LMP92064 is not set +# CONFIG_TI_ST is not set +# CONFIG_TI_TLC4541 is not set +# CONFIG_TI_TMAG5273 is not set +# CONFIG_TI_TSC2046 is not set +# CONFIG_TLAN is not set +# CONFIG_TLS is not set +# CONFIG_TLS_DEVICE is not set +# CONFIG_TLS_TOE is not set +# CONFIG_TMP006 is not set +# CONFIG_TMP007 is not set +# CONFIG_TMP117 is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_INODE64 is not set +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_TMPFS_QUOTA is not set +CONFIG_TMPFS_XATTR=y +# CONFIG_TOPSTAR_LAPTOP is not set +# CONFIG_TORTURE_TEST is not set +# CONFIG_TOSHIBA_HAPS is not set +# CONFIG_TOSHIBA_WMI is not set +# CONFIG_TOUCHSCREEN_88PM860X is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_ADC is not set +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AR1021_I2C is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_BU21029 is not set +# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set +# CONFIG_TOUCHSCREEN_CHIPONE_ICN8505 is not set +# CONFIG_TOUCHSCREEN_COLIBRI_VF50 is not set +# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP5 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_CYTTSP_SPI is not set +# CONFIG_TOUCHSCREEN_DA9034 is not set +# CONFIG_TOUCHSCREEN_DA9052 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set +# CONFIG_TOUCHSCREEN_EKTF2127 is not set +# CONFIG_TOUCHSCREEN_ELAN is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_EXC3000 is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GOODIX is not set +# CONFIG_TOUCHSCREEN_GOODIX_BERLIN_I2C is not set +# CONFIG_TOUCHSCREEN_GOODIX_BERLIN_SPI is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_HIDEEP is not set +# CONFIG_TOUCHSCREEN_HIMAX_HX83112B is not set +# CONFIG_TOUCHSCREEN_HP600 is not set +# CONFIG_TOUCHSCREEN_HP7XX is not set +# CONFIG_TOUCHSCREEN_HTCPEN is not set +# CONFIG_TOUCHSCREEN_HYCON_HY46XX is not set +# CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_ILITEK is not set +# CONFIG_TOUCHSCREEN_IMAGIS is not set +# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_IPAQ_MICRO is not set +# CONFIG_TOUCHSCREEN_IPROC is not set +# CONFIG_TOUCHSCREEN_IQS5XX is not set +# CONFIG_TOUCHSCREEN_IQS7211 is not set +# CONFIG_TOUCHSCREEN_LPC32XX is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MC13783 is not set +# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set +# CONFIG_TOUCHSCREEN_MIGOR is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_MMS114 is not set +# CONFIG_TOUCHSCREEN_MSG2638 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MX25 is not set +# CONFIG_TOUCHSCREEN_MXS_LRADC is not set +# CONFIG_TOUCHSCREEN_NOVATEK_NVT_TS is not set +# CONFIG_TOUCHSCREEN_PCAP is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_RASPBERRYPI_FW is not set +# CONFIG_TOUCHSCREEN_RM_TS is not set +# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set +# CONFIG_TOUCHSCREEN_S6SY761 is not set +# CONFIG_TOUCHSCREEN_SILEAD is not set +# CONFIG_TOUCHSCREEN_SIS_I2C is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_STMFTS is not set +# CONFIG_TOUCHSCREEN_STMPE is not set +# CONFIG_TOUCHSCREEN_SUN4I is not set +# CONFIG_TOUCHSCREEN_SUR40 is not set +# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set +# CONFIG_TOUCHSCREEN_SX8654 is not set +# CONFIG_TOUCHSCREEN_TI_AM335X_TSC is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +# CONFIG_TOUCHSCREEN_TS4800 is not set +# CONFIG_TOUCHSCREEN_TSC2004 is not set +# CONFIG_TOUCHSCREEN_TSC2005 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_TSC2007_IIO is not set +# CONFIG_TOUCHSCREEN_TSC200X_CORE is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_USB_3M is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_USB_DMC_TSC10 is not set +# CONFIG_TOUCHSCREEN_USB_E2I is not set +# CONFIG_TOUCHSCREEN_USB_EASYTOUCH is not set +# CONFIG_TOUCHSCREEN_USB_EGALAX is not set +# CONFIG_TOUCHSCREEN_USB_ELO is not set +# CONFIG_TOUCHSCREEN_USB_ETT_TC45USB is not set +# CONFIG_TOUCHSCREEN_USB_ETURBO is not set +# CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH is not set +# CONFIG_TOUCHSCREEN_USB_GOTOP is not set +# CONFIG_TOUCHSCREEN_USB_GUNZE is not set +# CONFIG_TOUCHSCREEN_USB_IDEALTEK is not set +# CONFIG_TOUCHSCREEN_USB_IRTOUCH is not set +# CONFIG_TOUCHSCREEN_USB_ITM is not set +# CONFIG_TOUCHSCREEN_USB_JASTEC is not set +# CONFIG_TOUCHSCREEN_USB_NEXIO is not set +# CONFIG_TOUCHSCREEN_USB_PANJIT is not set +# CONFIG_TOUCHSCREEN_USB_ZYTRONIC is not set +# CONFIG_TOUCHSCREEN_WACOM_I2C is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set +# CONFIG_TOUCHSCREEN_WM831X is not set +# CONFIG_TOUCHSCREEN_WM9705 is not set +# CONFIG_TOUCHSCREEN_WM9712 is not set +# CONFIG_TOUCHSCREEN_WM9713 is not set +# CONFIG_TOUCHSCREEN_WM97XX is not set +# CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE is not set +# CONFIG_TOUCHSCREEN_ZET6223 is not set +# CONFIG_TOUCHSCREEN_ZFORCE is not set +# CONFIG_TOUCHSCREEN_ZINITIX is not set +# CONFIG_TPL0102 is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TRACEPOINT_BENCHMARK is not set +# CONFIG_TRACER_SNAPSHOT is not set +# CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP is not set +# CONFIG_TRACE_BRANCH_PROFILING is not set +# CONFIG_TRACE_EVAL_MAP_FILE is not set +# CONFIG_TRACE_EVENT_INJECT is not set +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +# CONFIG_TRACE_MMIO_ACCESS is not set +CONFIG_TRACING_SUPPORT=y +CONFIG_TRAD_SIGNALS=y +# CONFIG_TRANSPARENT_HUGEPAGE is not set +# CONFIG_TREE_RCU is not set +# CONFIG_TRIM_UNUSED_KSYMS is not set +# CONFIG_TRUSTED_FOUNDATIONS is not set +# CONFIG_TRUSTED_KEYS is not set +# CONFIG_TRUSTED_KEYS_CAAM is not set +# CONFIG_TRUSTED_KEYS_DCP is not set +# CONFIG_TRUSTED_KEYS_TEE is not set +# CONFIG_TRUSTED_KEYS_TPM is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2591 is not set +# CONFIG_TSL2772 is not set +# CONFIG_TSL4531 is not set +# CONFIG_TSNEP is not set +# CONFIG_TSYS01 is not set +# CONFIG_TSYS02D is not set +# CONFIG_TTPCI_EEPROM is not set +CONFIG_TTY=y +# CONFIG_TTY_PRINTK is not set +# CONFIG_TUN is not set +# CONFIG_TUN_VNET_CROSS_LE is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL4030_MADC is not set +# CONFIG_TWL6030_GPADC is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_TXGBE is not set +# CONFIG_TYPEC is not set +# CONFIG_TYPEC_DP_ALTMODE is not set +# CONFIG_TYPEC_TCPM is not set +# CONFIG_TYPEC_UCSI is not set +# CONFIG_TYPHOON is not set +# CONFIG_UACCE is not set +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_UBIFS_ATIME_SUPPORT is not set +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +# CONFIG_UBIFS_FS_AUTHENTICATION is not set +CONFIG_UBIFS_FS_LZO=y +# CONFIG_UBIFS_FS_SECURITY is not set +CONFIG_UBIFS_FS_XATTR=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UBIFS_FS_ZSTD=y +# CONFIG_UBSAN is not set +CONFIG_UBSAN_ALIGNMENT=y +CONFIG_UBSAN_BOOL=y +# CONFIG_UBSAN_DIV_ZERO is not set +CONFIG_UBSAN_ENUM=y +CONFIG_UBSAN_SHIFT=y +# CONFIG_UBSAN_UNREACHABLE is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDMABUF is not set +CONFIG_UEVENT_HELPER=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_UFS_FS is not set +# CONFIG_UHID is not set +CONFIG_UID16=y +# CONFIG_UIMAGE_FIT_BLK is not set +# CONFIG_UIO is not set +# CONFIG_ULTRA is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_UNICODE is not set +CONFIG_UNIX=y +CONFIG_UNIX98_PTYS=y +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_UNIX_DIAG is not set +# CONFIG_UNWINDER_FRAME_POINTER is not set +# CONFIG_UPROBES is not set +# CONFIG_UPROBE_EVENTS is not set +# CONFIG_US5182D is not set +# CONFIG_USB is not set +# CONFIG_USB4 is not set +# CONFIG_USBIP_CORE is not set +CONFIG_USBIP_VHCI_HC_PORTS=8 +CONFIG_USBIP_VHCI_NR_HCS=1 +# CONFIG_USBIP_VUDC is not set +# CONFIG_USBPCWATCHDOG is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AIRSPY is not set +CONFIG_USB_ALI_M5632=y +# CONFIG_USB_AMD5536UDC is not set +CONFIG_USB_AN2720=y +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set +# CONFIG_USB_APPLEDISPLAY is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_ATM is not set +# CONFIG_USB_AUDIO is not set +CONFIG_USB_AUTOSUSPEND_DELAY=2 +# CONFIG_USB_BDC_UDC is not set +CONFIG_USB_BELKIN=y +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_CDNS3 is not set +# CONFIG_USB_CDNS3_IMX is not set +# CONFIG_USB_CDNS3_PCI_WRAP is not set +# CONFIG_USB_CDNSP_PCI is not set +# CONFIG_USB_CDNS_SUPPORT is not set +# CONFIG_USB_CHAOSKEY is not set +# CONFIG_USB_CHIPIDEA is not set +# CONFIG_USB_CHIPIDEA_GENERIC is not set +# CONFIG_USB_CHIPIDEA_IMX is not set +# CONFIG_USB_CHIPIDEA_MSM is not set +# CONFIG_USB_CHIPIDEA_NPCM is not set +# CONFIG_USB_CHIPIDEA_PCI is not set +# CONFIG_USB_CHIPIDEA_TEGRA is not set +# CONFIG_USB_CONFIGFS is not set +# CONFIG_USB_CONN_GPIO is not set +# CONFIG_USB_CXACRU is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +CONFIG_USB_DEFAULT_AUTHORIZATION_MODE=1 +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DUMMY_HCD is not set +# CONFIG_USB_DWC2 is not set +# CONFIG_USB_DWC2_DEBUG is not set +# CONFIG_USB_DWC2_DUAL_ROLE is not set +# CONFIG_USB_DWC2_HOST is not set +# CONFIG_USB_DWC2_PERIPHERAL is not set +# CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_DWC3_EXYNOS is not set +# CONFIG_USB_DWC3_HAPS is not set +# CONFIG_USB_DWC3_KEYSTONE is not set +# CONFIG_USB_DWC3_OCTEON is not set +# CONFIG_USB_DWC3_OF_SIMPLE is not set +# CONFIG_USB_DWC3_PCI is not set +# CONFIG_USB_DWC3_QCOM is not set +# CONFIG_USB_DWC3_ULPI is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_EG20T is not set +# CONFIG_USB_EHCI_FSL is not set +# CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_EHCI_HCD_AT91 is not set +# CONFIG_USB_EHCI_HCD_OMAP is not set +# CONFIG_USB_EHCI_HCD_PPC_OF is not set +# CONFIG_USB_EHCI_MV is not set +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +# CONFIG_USB_EHSET_TEST_FIXTURE is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_EZUSB_FX2 is not set +# CONFIG_USB_FEW_INIT_RETRIES is not set +# CONFIG_USB_FOTG210_HCD is not set +# CONFIG_USB_FOTG210_UDC is not set +# CONFIG_USB_FSL_USB2 is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_GADGET is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +CONFIG_USB_GADGET_VBUS_DRAW=2 +# CONFIG_USB_GADGET_XILINX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GOKU is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_GR_UDC is not set +# CONFIG_USB_GSPCA is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_DTCS033 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STK1135 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TOUPTEK is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_G_NOKIA is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_WEBCAM is not set +# CONFIG_USB_HACKRF is not set +# CONFIG_USB_HCD_TEST_MODE is not set +# CONFIG_USB_HID is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_HSIC_USB3503 is not set +# CONFIG_USB_HSIC_USB4604 is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_HUB_USB251XB is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_ISP1760 is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_KBD is not set +# CONFIG_USB_KC2190 is not set +# CONFIG_USB_LAN78XX is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_LEDS_TRIGGER_USBPORT is not set +# CONFIG_USB_LED_TRIG is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LGM_PHY is not set +# CONFIG_USB_LINK_LAYER_TEST is not set +# CONFIG_USB_M5602 is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_MAX3420_UDC is not set +# CONFIG_USB_MAX3421_HCD is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_MSI2500 is not set +# CONFIG_USB_MTU3 is not set +# CONFIG_USB_MUSB_GADGET is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MV_U3D is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_MXS_PHY is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_NET2280 is not set +# CONFIG_USB_NET_AQC111 is not set +# CONFIG_USB_NET_AX88179_178A is not set +# CONFIG_USB_NET_AX8817X is not set +# CONFIG_USB_NET_CDCETHER is not set +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_CDC_MBIM is not set +# CONFIG_USB_NET_CDC_NCM is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_CH9200 is not set +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_DM9601 is not set +CONFIG_USB_NET_DRIVERS=y +# CONFIG_USB_NET_GL620A is not set +# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_NET_KALMIA is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_QMI_WWAN is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_SR9700 is not set +# CONFIG_USB_NET_SR9800 is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_OHCI_HCD_PCI is not set +# CONFIG_USB_OHCI_HCD_PPC_OF is not set +# CONFIG_USB_OHCI_HCD_PPC_OF_BE is not set +# CONFIG_USB_OHCI_HCD_PPC_OF_LE is not set +# CONFIG_USB_OHCI_HCD_SSB is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_ONBOARD_DEV is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set +# CONFIG_USB_OTG_FSM is not set +# CONFIG_USB_OTG_PRODUCTLIST is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_PCI is not set +# CONFIG_USB_PCI_AMD is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_PHY is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_PWC_INPUT_EVDEV is not set +# CONFIG_USB_PXA27X is not set +# CONFIG_USB_QCOM_EUD is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_RAW_GADGET is not set +# CONFIG_USB_RENESAS_USBHS is not set +# CONFIG_USB_ROLES_INTEL_XHCI is not set +# CONFIG_USB_ROLE_SWITCH is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_RTL8152 is not set +# CONFIG_USB_RTL8153_ECM is not set +# CONFIG_USB_S2255 is not set +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_CH348 is not set +# CONFIG_USB_SERIAL_CP210X is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_F81232 is not set +# CONFIG_USB_SERIAL_F8153X is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_GARMIN is not set +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_IUU is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_METRO is not set +# CONFIG_USB_SERIAL_MOS7715_PARPORT is not set +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_MXUPORT is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_OPTICON is not set +# CONFIG_USB_SERIAL_OPTION is not set +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_QCAUX is not set +# CONFIG_USB_SERIAL_QT2 is not set +# CONFIG_USB_SERIAL_QUALCOMM is not set +# CONFIG_USB_SERIAL_SAFE is not set +CONFIG_USB_SERIAL_SAFE_PADDED=y +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_SIMPLE is not set +# CONFIG_USB_SERIAL_SPCP8X5 is not set +# CONFIG_USB_SERIAL_SSU100 is not set +# CONFIG_USB_SERIAL_SYMBOL is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_UPD78F0730 is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_WISHBONE is not set +# CONFIG_USB_SERIAL_XR is not set +# CONFIG_USB_SERIAL_XSENS_MT is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_SNP_UDC_PLAT is not set +# CONFIG_USB_SPEEDTOUCH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_TMC is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_UAS is not set +# CONFIG_USB_UEAGLEATM is not set +# CONFIG_USB_ULPI is not set +# CONFIG_USB_ULPI_BUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set +# CONFIG_USB_VIDEO_CLASS is not set +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +# CONFIG_USB_VL600 is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_XEN_HCD is not set +# CONFIG_USB_XHCI_DBGCAP is not set +# CONFIG_USB_XHCI_HCD is not set +# CONFIG_USB_XHCI_MVEBU is not set +# CONFIG_USB_XHCI_PCI_RENESAS is not set +# CONFIG_USB_XUSBATM is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_ZERO is not set +# CONFIG_USELIB is not set +# CONFIG_USERFAULTFD is not set +# CONFIG_USERIO is not set +# CONFIG_USER_DECRYPTED_DATA is not set +# CONFIG_USER_EVENTS is not set +# CONFIG_USE_OF is not set +# CONFIG_UTS_NS is not set +# CONFIG_U_SERIAL_CONSOLE is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_V4L_PLATFORM_DRIVERS is not set +# CONFIG_V4L_TEST_DRIVERS is not set +# CONFIG_VALIDATE_FS_PARSER is not set +# CONFIG_VBOXGUEST is not set +# CONFIG_VCAP is not set +# CONFIG_VCNL3020 is not set +# CONFIG_VCNL4000 is not set +# CONFIG_VCNL4035 is not set +# CONFIG_VCPU_STALL_DETECTOR is not set +# CONFIG_VDPA is not set +CONFIG_VDSO=y +# CONFIG_VEML6030 is not set +# CONFIG_VEML6040 is not set +# CONFIG_VEML6070 is not set +# CONFIG_VEML6075 is not set +# CONFIG_VETH is not set +# CONFIG_VEXPRESS_CONFIG is not set +# CONFIG_VF610_ADC is not set +# CONFIG_VF610_DAC is not set +# CONFIG_VFAT_FS is not set +# CONFIG_VFIO is not set +# CONFIG_VFIO_DEBUGFS is not set +# CONFIG_VFIO_FSL_MC is not set +# CONFIG_VFIO_PLATFORM is not set +# CONFIG_VGASTATE is not set +# CONFIG_VGA_ARB is not set +# CONFIG_VGA_CONSOLE is not set +# CONFIG_VGA_SWITCHEROO is not set +# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set +CONFIG_VHOST_ENABLE_FORK_OWNER_CONTROL=y +CONFIG_VHOST_MENU=y +# CONFIG_VHOST_NET is not set +# CONFIG_VHOST_VSOCK is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_VIDEO_AD5820 is not set +# CONFIG_VIDEO_ADP1653 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_ADV7393 is not set +# CONFIG_VIDEO_ADV748X is not set +# CONFIG_VIDEO_ADV7511 is not set +# CONFIG_VIDEO_ADV7604 is not set +# CONFIG_VIDEO_ADV7842 is not set +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_AK7375 is not set +# CONFIG_VIDEO_AK881X is not set +# CONFIG_VIDEO_ALVIUM_CSI2 is not set +# CONFIG_VIDEO_AM437X_VPFE is not set +# CONFIG_VIDEO_AR0521 is not set +# CONFIG_VIDEO_ASPEED is not set +# CONFIG_VIDEO_ATMEL_ISC is not set +# CONFIG_VIDEO_ATMEL_ISI is not set +# CONFIG_VIDEO_AU0828 is not set +# CONFIG_VIDEO_BCM2835 is not set +# CONFIG_VIDEO_BCM2835_UNICAM is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_CADENCE_CSI2RX is not set +# CONFIG_VIDEO_CADENCE_CSI2TX is not set +# CONFIG_VIDEO_CAFE_CCIC is not set +# CONFIG_VIDEO_CAMERA_SENSOR is not set +# CONFIG_VIDEO_CCS is not set +# CONFIG_VIDEO_COBALT is not set +# CONFIG_VIDEO_CODA is not set +# CONFIG_VIDEO_CS3308 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_CX231XX is not set +# CONFIG_VIDEO_CX2341X is not set +# CONFIG_VIDEO_CX25821 is not set +# CONFIG_VIDEO_CX25840 is not set +# CONFIG_VIDEO_CX88 is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_VIDEO_DS90UB913 is not set +# CONFIG_VIDEO_DS90UB953 is not set +# CONFIG_VIDEO_DS90UB960 is not set +# CONFIG_VIDEO_DT3155 is not set +# CONFIG_VIDEO_DW9714 is not set +# CONFIG_VIDEO_DW9719 is not set +# CONFIG_VIDEO_DW9768 is not set +# CONFIG_VIDEO_DW9807_VCM is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_ET8EK8 is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_GC0308 is not set +# CONFIG_VIDEO_GC05A2 is not set +# CONFIG_VIDEO_GC08A3 is not set +# CONFIG_VIDEO_GC2145 is not set +# CONFIG_VIDEO_GO7007 is not set +# CONFIG_VIDEO_GS1662 is not set +# CONFIG_VIDEO_HDPVR is not set +# CONFIG_VIDEO_HEXIUM_GEMINI is not set +# CONFIG_VIDEO_HEXIUM_ORION is not set +# CONFIG_VIDEO_HI556 is not set +# CONFIG_VIDEO_HI846 is not set +# CONFIG_VIDEO_HI847 is not set +# CONFIG_VIDEO_I2C is not set +# CONFIG_VIDEO_IMX208 is not set +# CONFIG_VIDEO_IMX214 is not set +# CONFIG_VIDEO_IMX219 is not set +# CONFIG_VIDEO_IMX258 is not set +# CONFIG_VIDEO_IMX274 is not set +# CONFIG_VIDEO_IMX283 is not set +# CONFIG_VIDEO_IMX290 is not set +# CONFIG_VIDEO_IMX296 is not set +# CONFIG_VIDEO_IMX319 is not set +# CONFIG_VIDEO_IMX334 is not set +# CONFIG_VIDEO_IMX335 is not set +# CONFIG_VIDEO_IMX355 is not set +# CONFIG_VIDEO_IMX412 is not set +# CONFIG_VIDEO_IMX415 is not set +# CONFIG_VIDEO_IMX500 is not set +# CONFIG_VIDEO_IMX7_CSI is not set +# CONFIG_VIDEO_IMX8MQ_MIPI_CSI2 is not set +# CONFIG_VIDEO_IMX8_ISI is not set +# CONFIG_VIDEO_IMX8_JPEG is not set +# CONFIG_VIDEO_IMX_MIPI_CSIS is not set +# CONFIG_VIDEO_IMX_PXP is not set +# CONFIG_VIDEO_INTEL_IPU6 is not set +# CONFIG_VIDEO_IPU3_CIO2 is not set +# CONFIG_VIDEO_IR_I2C is not set +# CONFIG_VIDEO_ISL7998X is not set +# CONFIG_VIDEO_IVTV is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_LM3560 is not set +# CONFIG_VIDEO_LM3646 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_VIDEO_MAX9286 is not set +# CONFIG_VIDEO_MAX96714 is not set +# CONFIG_VIDEO_MAX96717 is not set +# CONFIG_VIDEO_MEM2MEM_DEINTERLACE is not set +# CONFIG_VIDEO_MGB4 is not set +# CONFIG_VIDEO_ML86V7667 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_MT9M001 is not set +# CONFIG_VIDEO_MT9M111 is not set +# CONFIG_VIDEO_MT9M114 is not set +# CONFIG_VIDEO_MT9P031 is not set +# CONFIG_VIDEO_MT9T112 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_MT9V032 is not set +# CONFIG_VIDEO_MT9V111 is not set +# CONFIG_VIDEO_MUX is not set +# CONFIG_VIDEO_MXB is not set +# CONFIG_VIDEO_OG01A1B is not set +# CONFIG_VIDEO_OMAP2_VOUT is not set +# CONFIG_VIDEO_OV01A10 is not set +# CONFIG_VIDEO_OV02A10 is not set +# CONFIG_VIDEO_OV08D10 is not set +# CONFIG_VIDEO_OV08X40 is not set +# CONFIG_VIDEO_OV13858 is not set +# CONFIG_VIDEO_OV13B10 is not set +# CONFIG_VIDEO_OV2311 is not set +# CONFIG_VIDEO_OV2640 is not set +# CONFIG_VIDEO_OV2659 is not set +# CONFIG_VIDEO_OV2680 is not set +# CONFIG_VIDEO_OV2685 is not set +# CONFIG_VIDEO_OV2740 is not set +# CONFIG_VIDEO_OV4689 is not set +# CONFIG_VIDEO_OV5640 is not set +# CONFIG_VIDEO_OV5645 is not set +# CONFIG_VIDEO_OV5647 is not set +# CONFIG_VIDEO_OV5648 is not set +# CONFIG_VIDEO_OV5670 is not set +# CONFIG_VIDEO_OV5675 is not set +# CONFIG_VIDEO_OV5693 is not set +# CONFIG_VIDEO_OV5695 is not set +# CONFIG_VIDEO_OV64A40 is not set +# CONFIG_VIDEO_OV6650 is not set +# CONFIG_VIDEO_OV7251 is not set +# CONFIG_VIDEO_OV7640 is not set +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_OV772X is not set +# CONFIG_VIDEO_OV7740 is not set +# CONFIG_VIDEO_OV8856 is not set +# CONFIG_VIDEO_OV8858 is not set +# CONFIG_VIDEO_OV8865 is not set +# CONFIG_VIDEO_OV9282 is not set +# CONFIG_VIDEO_OV9640 is not set +# CONFIG_VIDEO_OV9650 is not set +# CONFIG_VIDEO_OV9734 is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_RASPBERRYPI_PISP_BE is not set +# CONFIG_VIDEO_RCAR_CSI2 is not set +# CONFIG_VIDEO_RCAR_ISP is not set +# CONFIG_VIDEO_RCAR_VIN is not set +# CONFIG_VIDEO_RDACM20 is not set +# CONFIG_VIDEO_RDACM21 is not set +# CONFIG_VIDEO_RJ54N1 is not set +# CONFIG_VIDEO_ROCKCHIP_ISP1 is not set +# CONFIG_VIDEO_S5C73M3 is not set +# CONFIG_VIDEO_S5K5BAF is not set +# CONFIG_VIDEO_S5K6A3 is not set +# CONFIG_VIDEO_SAA6588 is not set +# CONFIG_VIDEO_SAA6752HS is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7134 is not set +# CONFIG_VIDEO_SAA7164 is not set +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_SOLO6X10 is not set +# CONFIG_VIDEO_SONY_BTF_MPX is not set +# CONFIG_VIDEO_STK1160 is not set +# CONFIG_VIDEO_ST_MIPID02 is not set +# CONFIG_VIDEO_SUN4I_CSI is not set +# CONFIG_VIDEO_SUN6I_CSI is not set +# CONFIG_VIDEO_SUN8I_A83T_MIPI_CSI2 is not set +# CONFIG_VIDEO_TC358743 is not set +# CONFIG_VIDEO_TC358746 is not set +# CONFIG_VIDEO_TDA1997X is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_THP7312 is not set +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_THS8200 is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_TW2804 is not set +# CONFIG_VIDEO_TW5864 is not set +# CONFIG_VIDEO_TW68 is not set +# CONFIG_VIDEO_TW9900 is not set +# CONFIG_VIDEO_TW9903 is not set +# CONFIG_VIDEO_TW9906 is not set +# CONFIG_VIDEO_TW9910 is not set +# CONFIG_VIDEO_UDA1342 is not set +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set +# CONFIG_VIDEO_USBTV is not set +# CONFIG_VIDEO_VGXY61 is not set +# CONFIG_VIDEO_VP27SMPX is not set +# CONFIG_VIDEO_VPX3220 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_XILINX is not set +# CONFIG_VIDEO_ZORAN is not set +# CONFIG_VIRTIO_BALLOON is not set +# CONFIG_VIRTIO_CONSOLE is not set +# CONFIG_VIRTIO_FS is not set +# CONFIG_VIRTIO_INPUT is not set +CONFIG_VIRTIO_MENU=y +# CONFIG_VIRTIO_MMIO is not set +# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set +# CONFIG_VIRTIO_PCI is not set +# CONFIG_VIRTIO_VFIO_PCI is not set +# CONFIG_VIRTUALIZATION is not set +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_VL53L0X_I2C is not set +# CONFIG_VL6180 is not set +CONFIG_VLAN_8021Q=y +# CONFIG_VLAN_8021Q_GVRP is not set +# CONFIG_VLAN_8021Q_MVRP is not set +# CONFIG_VMAP_STACK is not set +# CONFIG_VME_BUS is not set +# CONFIG_VMLINUX_MAP is not set +# CONFIG_VMSPLIT_1G is not set +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_2G_OPT is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_3G_OPT is not set +# CONFIG_VMWARE_PVSCSI is not set +# CONFIG_VMWARE_VMCI is not set +# CONFIG_VMXNET3 is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_VORTEX is not set +# CONFIG_VSOCKETS is not set +# CONFIG_VSOCKETS_DIAG is not set +# CONFIG_VT is not set +# CONFIG_VT6655 is not set +# CONFIG_VT6656 is not set +# CONFIG_VXFS_FS is not set +# CONFIG_VXLAN is not set +# CONFIG_VZ89X is not set +# CONFIG_W1 is not set +# CONFIG_W1_CON is not set +# CONFIG_W1_MASTER_AMD_AXI is not set +# CONFIG_W1_MASTER_DS2482 is not set +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_GPIO is not set +# CONFIG_W1_MASTER_MATROX is not set +# CONFIG_W1_MASTER_SGI is not set +# CONFIG_W1_MASTER_UART is not set +# CONFIG_W1_SLAVE_DS2405 is not set +# CONFIG_W1_SLAVE_DS2406 is not set +# CONFIG_W1_SLAVE_DS2408 is not set +# CONFIG_W1_SLAVE_DS2413 is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2430 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2438 is not set +# CONFIG_W1_SLAVE_DS250X is not set +# CONFIG_W1_SLAVE_DS2780 is not set +# CONFIG_W1_SLAVE_DS2781 is not set +# CONFIG_W1_SLAVE_DS2805 is not set +# CONFIG_W1_SLAVE_DS28E04 is not set +# CONFIG_W1_SLAVE_DS28E17 is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W83627HF_WDT is not set +# CONFIG_W83877F_WDT is not set +# CONFIG_W83977F_WDT is not set +# CONFIG_WAN is not set +# CONFIG_WANXL is not set +# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_CORE is not set +CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y +# CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT is not set +# CONFIG_WATCHDOG_NOWAYOUT is not set +CONFIG_WATCHDOG_OPEN_TIMEOUT=0 +# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set +# CONFIG_WATCHDOG_SYSFS is not set +# CONFIG_WATCH_QUEUE is not set +# CONFIG_WD80x3 is not set +# CONFIG_WDAT_WDT is not set +# CONFIG_WDTPCI is not set +# CONFIG_WERROR is not set +# CONFIG_WEXT_CORE is not set +# CONFIG_WEXT_PRIV is not set +# CONFIG_WEXT_PROC is not set +# CONFIG_WEXT_SPY is not set +# CONFIG_WIREGUARD is not set +CONFIG_WIRELESS=y +# CONFIG_WIRELESS_EXT is not set +# CONFIG_WIZNET_W5100 is not set +# CONFIG_WIZNET_W5300 is not set +# CONFIG_WL1251 is not set +# CONFIG_WL12XX is not set +# CONFIG_WL18XX is not set +CONFIG_WLAN=y +# CONFIG_WLAN_VENDOR_ADMTEK is not set +# CONFIG_WLAN_VENDOR_ATH is not set +# CONFIG_WLAN_VENDOR_ATMEL is not set +# CONFIG_WLAN_VENDOR_BROADCOM is not set +# CONFIG_WLAN_VENDOR_INTEL is not set +# CONFIG_WLAN_VENDOR_INTERSIL is not set +# CONFIG_WLAN_VENDOR_MARVELL is not set +# CONFIG_WLAN_VENDOR_MEDIATEK is not set +# CONFIG_WLAN_VENDOR_MICROCHIP is not set +# CONFIG_WLAN_VENDOR_PURELIFI is not set +# CONFIG_WLAN_VENDOR_QUANTENNA is not set +# CONFIG_WLAN_VENDOR_RALINK is not set +# CONFIG_WLAN_VENDOR_REALTEK is not set +# CONFIG_WLAN_VENDOR_RSI is not set +# CONFIG_WLAN_VENDOR_SILABS is not set +# CONFIG_WLAN_VENDOR_ST is not set +# CONFIG_WLAN_VENDOR_TI is not set +# CONFIG_WLAN_VENDOR_ZYDAS is not set +# CONFIG_WLCORE is not set +# CONFIG_WMI_BMOF is not set +# CONFIG_WPCM450_SOC is not set +# CONFIG_WQ_CPU_INTENSIVE_REPORT is not set +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +# CONFIG_WQ_WATCHDOG is not set +# CONFIG_WWAN is not set +# CONFIG_WWAN_HWSIM is not set +# CONFIG_WW_MUTEX_SELFTEST is not set +# CONFIG_X25 is not set +# CONFIG_X509_CERTIFICATE_PARSER is not set +# CONFIG_X86_KERNEL_IBT is not set +# CONFIG_X86_PKG_TEMP_THERMAL is not set +# CONFIG_X86_USER_SHADOW_STACK is not set +# CONFIG_X9250 is not set +# CONFIG_XDP_SOCKETS is not set +# CONFIG_XEN is not set +# CONFIG_XEN_GRANT_DMA_ALLOC is not set +# CONFIG_XEN_PVCALLS_FRONTEND is not set +CONFIG_XEN_SCRUB_PAGES_DEFAULT=y +CONFIG_XFRM=y +# CONFIG_XFRM_INTERFACE is not set +# CONFIG_XFRM_IPCOMP is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_USER is not set +# CONFIG_XFS_DEBUG is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_ONLINE_SCRUB is not set +# CONFIG_XFS_POSIX_ACL is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_SUPPORT_ASCII_CI is not set +# CONFIG_XFS_SUPPORT_V4 is not set +# CONFIG_XFS_WARN is not set +# CONFIG_XIAOMI_WMI is not set +# CONFIG_XILINX_AXI_EMAC is not set +# CONFIG_XILINX_DMA is not set +# CONFIG_XILINX_EMACLITE is not set +# CONFIG_XILINX_GMII2RGMII is not set +# CONFIG_XILINX_INTC is not set +# CONFIG_XILINX_LL_TEMAC is not set +# CONFIG_XILINX_SDFEC is not set +# CONFIG_XILINX_VCU is not set +# CONFIG_XILINX_WATCHDOG is not set +# CONFIG_XILINX_WINDOW_WATCHDOG is not set +# CONFIG_XILINX_XADC is not set +# CONFIG_XILINX_XDMA is not set +# CONFIG_XILINX_ZYNQMP_DMA is not set +# CONFIG_XILINX_ZYNQMP_DPDMA is not set +# CONFIG_XILLYBUS is not set +# CONFIG_XILLYUSB is not set +# CONFIG_XIL_AXIS_FIFO is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_XMON is not set +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_ARM is not set +# CONFIG_XZ_DEC_ARM64 is not set +# CONFIG_XZ_DEC_ARMTHUMB is not set +# CONFIG_XZ_DEC_BCJ is not set +# CONFIG_XZ_DEC_MICROLZMA is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_RISCV is not set +# CONFIG_XZ_DEC_SPARC is not set +# CONFIG_XZ_DEC_TEST is not set +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_YAM is not set +# CONFIG_YAMAHA_YAS530 is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_YENTA is not set +# CONFIG_YENTA_O2 is not set +# CONFIG_YENTA_RICOH is not set +# CONFIG_YENTA_TI is not set +# CONFIG_YENTA_TOSHIBA is not set +# CONFIG_YOGABOOK is not set +# CONFIG_ZBUD is not set +# CONFIG_ZD1211RW is not set +# CONFIG_ZD1211RW_DEBUG is not set +# CONFIG_ZEROPLUS_FF is not set +# CONFIG_ZERO_CALL_USED_REGS is not set +# CONFIG_ZIIRAVE_WATCHDOG is not set +# CONFIG_ZISOFS is not set +# CONFIG_ZLIB_DEFLATE is not set +# CONFIG_ZLIB_INFLATE is not set +CONFIG_ZONE_DMA=y +# CONFIG_ZOPT2201 is not set +# CONFIG_ZPA2326 is not set +# CONFIG_ZPOOL is not set +# CONFIG_ZRAM is not set +# CONFIG_ZRAM_BACKEND_842 is not set +# CONFIG_ZRAM_BACKEND_DEFLATE is not set +# CONFIG_ZRAM_BACKEND_LZ4 is not set +# CONFIG_ZRAM_BACKEND_LZ4HC is not set +# CONFIG_ZRAM_BACKEND_LZO is not set +# CONFIG_ZRAM_BACKEND_ZSTD is not set +CONFIG_ZRAM_DEF_COMP="unset-value" +# CONFIG_ZRAM_DEF_COMP_842 is not set +# CONFIG_ZRAM_DEF_COMP_LZ4 is not set +# CONFIG_ZRAM_DEF_COMP_LZ4HC is not set +# CONFIG_ZRAM_DEF_COMP_LZO is not set +# CONFIG_ZRAM_DEF_COMP_LZORLE is not set +# CONFIG_ZRAM_DEF_COMP_ZSTD is not set +# CONFIG_ZRAM_MEMORY_TRACKING is not set +# CONFIG_ZRAM_MULTI_COMP is not set +# CONFIG_ZRAM_TRACK_ENTRY_ACTIME is not set +# CONFIG_ZSMALLOC is not set +CONFIG_ZSMALLOC_CHAIN_SIZE=8 +# CONFIG_ZSWAP is not set diff --git a/target/linux/generic/hack-6.12/200-tools_portability.patch b/target/linux/generic/hack-6.12/200-tools_portability.patch new file mode 100644 index 00000000000..f25a1321272 --- /dev/null +++ b/target/linux/generic/hack-6.12/200-tools_portability.patch @@ -0,0 +1,176 @@ +From a7ae4ed0a3951c45d4a59ee575951b64ae4a23fb Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Tue, 7 May 2024 12:22:15 +0200 +Subject: [PATCH] kernel: fix tools build breakage on macos with x86 + +Signed-off-by: Felix Fietkau +--- +--- a/tools/scripts/Makefile.include ++++ b/tools/scripts/Makefile.include +@@ -72,8 +72,6 @@ $(call allow-override,CXX,$(CROSS_COMPIL + $(call allow-override,STRIP,$(CROSS_COMPILE)strip) + endif + +-CC_NO_CLANG := $(shell $(CC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?) +- + ifneq ($(LLVM),) + HOSTAR ?= $(LLVM_PREFIX)llvm-ar$(LLVM_SUFFIX) + HOSTCC ?= $(LLVM_PREFIX)clang$(LLVM_SUFFIX) +@@ -84,6 +82,9 @@ HOSTCC ?= gcc + HOSTLD ?= ld + endif + ++CC_NO_CLANG := $(shell $(CC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?) ++HOSTCC_NO_CLANG := $(shell $(HOSTCC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?) ++ + # Some tools require Clang, LLC and/or LLVM utils + CLANG ?= clang + LLC ?= llc +@@ -92,8 +93,9 @@ LLVM_OBJCOPY ?= llvm-objcopy + LLVM_STRIP ?= llvm-strip + + ifeq ($(CC_NO_CLANG), 1) +-EXTRA_WARNINGS += -Wstrict-aliasing=3 +- ++ ifeq ($(HOSTCC_NO_CLANG), 1) ++ EXTRA_WARNINGS += -Wstrict-aliasing=3 ++ endif + else ifneq ($(CROSS_COMPILE),) + # Allow userspace to override CLANG_CROSS_FLAGS to specify their own + # sysroots and flags or to avoid the GCC call in pure Clang builds. +--- a/tools/include/linux/types.h ++++ b/tools/include/linux/types.h +@@ -56,6 +56,7 @@ typedef __s8 s8; + #define __user + #endif + #define __must_check ++#undef __cold + #define __cold + + typedef __u16 __bitwise __le16; +--- a/tools/objtool/include/objtool/objtool.h ++++ b/tools/objtool/include/objtool/objtool.h +@@ -12,6 +12,7 @@ + + #include + ++#undef __weak + #define __weak __attribute__((weak)) + + struct pv_state { +--- a/tools/include/asm-generic/bitops/fls.h ++++ b/tools/include/asm-generic/bitops/fls.h +@@ -2,6 +2,8 @@ + #ifndef _ASM_GENERIC_BITOPS_FLS_H_ + #define _ASM_GENERIC_BITOPS_FLS_H_ + ++#include ++ + /** + * generic_fls - find last (most-significant) bit set + * @x: the word to search +@@ -10,6 +12,7 @@ + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ + ++#define generic_fls __linux_fls + static __always_inline int generic_fls(unsigned int x) + { + int r = 32; +--- a/tools/lib/string.c ++++ b/tools/lib/string.c +@@ -96,6 +96,7 @@ int strtobool(const char *s, bool *res) + * If libc has strlcpy() then that version will override this + * implementation: + */ ++#ifndef __APPLE__ + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wignored-attributes" +@@ -114,6 +115,7 @@ size_t __weak strlcpy(char *dest, const + #ifdef __clang__ + #pragma clang diagnostic pop + #endif ++#endif + + /** + * skip_spaces - Removes leading whitespace from @str. +--- a/tools/arch/x86/include/asm/insn.h ++++ b/tools/arch/x86/include/asm/insn.h +@@ -7,7 +7,7 @@ + * Copyright (C) IBM Corporation, 2009 + */ + +-#include ++#include + /* insn_attr_t is defined in inat.h */ + #include "inat.h" /* __ignore_sync_check__ */ + +--- a/tools/arch/x86/include/asm/orc_types.h ++++ b/tools/arch/x86/include/asm/orc_types.h +@@ -46,7 +46,17 @@ + #define ORC_TYPE_REGS_PARTIAL 4 + + #ifndef __ASSEMBLY__ ++#ifdef __APPLE__ ++#include ++ ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++#define __LITTLE_ENDIAN_BITFIELD ++#elif __BYTE_ORDER == __BIG_ENDIAN ++#define __BIG_ENDIAN_BITFIELD ++#endif ++#else + #include ++#endif + + /* + * This struct is more or less a vastly simplified version of the DWARF Call +--- a/tools/include/linux/rbtree.h ++++ b/tools/include/linux/rbtree.h +@@ -18,7 +18,6 @@ + #define __TOOLS_LINUX_PERF_RBTREE_H + + #include +-#include + + struct rb_node { + unsigned long __rb_parent_color; +--- a/tools/lib/subcmd/exec-cmd.c ++++ b/tools/lib/subcmd/exec-cmd.c +@@ -12,7 +12,10 @@ + #include "subcmd-config.h" + + #define MAX_ARGS 32 ++ ++#ifndef PATH_MAX + #define PATH_MAX 4096 ++#endif + + static const char *argv_exec_path; + static const char *argv0_path; +--- a/tools/objtool/Makefile ++++ b/tools/objtool/Makefile +@@ -41,6 +41,8 @@ OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBS + elfshdr := $(shell echo '$(pound)include ' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - | grep elf_getshdr) + OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED) + ++OBJTOOL_CFLAGS += $(HOST_EXTRACFLAGS) ++ + # Always want host compilation. + HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)" + +--- a/tools/arch/x86/lib/insn.c ++++ b/tools/arch/x86/lib/insn.c +@@ -15,7 +15,11 @@ + #include "../include/asm/insn.h" /* __ignore_sync_check__ */ + #include /* __ignore_sync_check__ */ + ++#ifdef __KERNEL__ + #include ++#else ++#include ++#endif + #include + + #include "../include/asm/emulate_prefix.h" /* __ignore_sync_check__ */ diff --git a/target/linux/generic/hack-6.12/204-module_strip.patch b/target/linux/generic/hack-6.12/204-module_strip.patch new file mode 100644 index 00000000000..6f6f6a70d9e --- /dev/null +++ b/target/linux/generic/hack-6.12/204-module_strip.patch @@ -0,0 +1,201 @@ +From a779a482fb9b9f8fcdf8b2519c789b4b9bb5dd05 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 16:56:48 +0200 +Subject: build: add a hack for removing non-essential module info + +Signed-off-by: Felix Fietkau +--- + include/linux/module.h | 13 ++++++++----- + include/linux/moduleparam.h | 15 ++++++++++++--- + init/Kconfig | 7 +++++++ + kernel/module.c | 5 ++++- + scripts/mod/modpost.c | 12 ++++++++++++ + 5 files changed, 43 insertions(+), 9 deletions(-) + +--- a/include/linux/module.h ++++ b/include/linux/module.h +@@ -166,6 +166,7 @@ struct module_kobject *lookup_or_create_ + + /* Generic info of form tag = "info" */ + #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info) ++#define MODULE_INFO_STRIP(tag, info) __MODULE_INFO_STRIP(tag, tag, info) + + /* For userspace: you can also call me... */ + #define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias) +@@ -241,12 +242,12 @@ struct module_kobject *lookup_or_create_ + * Author(s), use "Name " or just "Name", for multiple + * authors use multiple MODULE_AUTHOR() statements/lines. + */ +-#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author) ++#define MODULE_AUTHOR(_author) MODULE_INFO_STRIP(author, _author) + + /* What your module does. */ +-#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description) ++#define MODULE_DESCRIPTION(_description) MODULE_INFO_STRIP(description, _description) + +-#ifdef MODULE ++#if defined(MODULE) && !defined(CONFIG_MODULE_STRIPPED) + /* Creates an alias so file2alias.c can find device table. */ + #define MODULE_DEVICE_TABLE(type, name) \ + extern typeof(name) __mod_##type##__##name##_device_table \ +@@ -273,7 +274,9 @@ extern typeof(name) __mod_##type##__##na + */ + + #if defined(MODULE) || !defined(CONFIG_SYSFS) +-#define MODULE_VERSION(_version) MODULE_INFO(version, _version) ++#define MODULE_VERSION(_version) MODULE_INFO_STRIP(version, _version) ++#elif defined(CONFIG_MODULE_STRIPPED) ++#define MODULE_VERSION(_version) __MODULE_INFO_DISABLED(version) + #else + #define MODULE_VERSION(_version) \ + MODULE_INFO(version, _version); \ +@@ -296,7 +299,7 @@ extern typeof(name) __mod_##type##__##na + /* Optional firmware file (or files) needed by the module + * format is simply firmware file name. Multiple firmware + * files require multiple MODULE_FIRMWARE() specifiers */ +-#define MODULE_FIRMWARE(_firmware) MODULE_INFO(firmware, _firmware) ++#define MODULE_FIRMWARE(_firmware) MODULE_INFO_STRIP(firmware, _firmware) + + #define MODULE_IMPORT_NS(ns) MODULE_INFO(import_ns, __stringify(ns)) + +--- a/include/linux/moduleparam.h ++++ b/include/linux/moduleparam.h +@@ -20,6 +20,16 @@ + /* Chosen so that structs with an unsigned long line up. */ + #define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long)) + ++/* This struct is here for syntactic coherency, it is not used */ ++#define __MODULE_INFO_DISABLED(name) \ ++ struct __UNIQUE_ID(name) {} ++ ++#ifdef CONFIG_MODULE_STRIPPED ++#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO_DISABLED(name) ++#else ++#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO(tag, name, info) ++#endif ++ + #define __MODULE_INFO(tag, name, info) \ + static const char __UNIQUE_ID(name)[] \ + __used __section(".modinfo") __aligned(1) \ +@@ -31,7 +41,7 @@ + /* One for each parameter, describing how to use it. Some files do + multiple of these per line, so can't just use MODULE_INFO. */ + #define MODULE_PARM_DESC(_parm, desc) \ +- __MODULE_INFO(parm, _parm, #_parm ":" desc) ++ __MODULE_INFO_STRIP(parm, _parm, #_parm ":" desc) + + struct kernel_param; + +--- a/kernel/module/Kconfig ++++ b/kernel/module/Kconfig +@@ -402,4 +402,11 @@ config MODULES_TREE_LOOKUP + def_bool y + depends on PERF_EVENTS || TRACING || CFI_CLANG + ++config MODULE_STRIPPED ++ bool "Reduce module size" ++ depends on MODULES ++ help ++ Remove module parameter descriptions, author info, version, aliases, ++ device tables, etc. ++ + endif # MODULES +--- a/kernel/module/main.c ++++ b/kernel/module/main.c +@@ -1001,6 +1001,7 @@ size_t modinfo_attrs_count = ARRAY_SIZE( + + static const char vermagic[] = VERMAGIC_STRING; + ++#if defined(CONFIG_MODVERSIONS) || !defined(CONFIG_MODULE_STRIPPED) + int try_to_force_load(struct module *mod, const char *reason) + { + #ifdef CONFIG_MODULE_FORCE_LOAD +@@ -1012,6 +1013,7 @@ int try_to_force_load(struct module *mod + return -ENOEXEC; + #endif + } ++#endif + + /* Parse tag=value strings from .modinfo section */ + char *module_next_tag_pair(char *string, unsigned long *secsize) +@@ -2095,9 +2097,11 @@ static void module_augment_kernel_taints + + static int check_modinfo(struct module *mod, struct load_info *info, int flags) + { +- const char *modmagic = get_modinfo(info, "vermagic"); + int err; + ++#ifndef CONFIG_MODULE_STRIPPED ++ const char *modmagic = get_modinfo(info, "vermagic"); ++ + if (flags & MODULE_INIT_IGNORE_VERMAGIC) + modmagic = NULL; + +@@ -2111,6 +2115,7 @@ static int check_modinfo(struct module * + info->name, modmagic, vermagic); + return -ENOEXEC; + } ++#endif + + err = check_modinfo_livepatch(mod, info); + if (err) +--- a/scripts/mod/modpost.c ++++ b/scripts/mod/modpost.c +@@ -1605,7 +1605,9 @@ static void read_symbols(const char *mod + symname = remove_dot(info.strtab + sym->st_name); + + handle_symbol(mod, &info, sym, symname); ++#ifndef CONFIG_MODULE_STRIPPED + handle_moddevtable(mod, &info, sym, symname); ++#endif + } + + check_sec_ref(mod, &info); +@@ -1762,7 +1764,9 @@ static void add_header(struct buffer *b, + buf_printf(b, "#include \n"); + buf_printf(b, "#include \n"); + buf_printf(b, "\n"); ++#ifndef CONFIG_MODULE_STRIPPED + buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n"); ++#endif + buf_printf(b, "\n"); + buf_printf(b, "__visible struct module __this_module\n"); + buf_printf(b, "__section(\".gnu.linkonce.this_module\") = {\n"); +@@ -1776,11 +1780,13 @@ static void add_header(struct buffer *b, + buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n"); + buf_printf(b, "};\n"); + ++#ifndef CONFIG_MODULE_STRIPPED + if (!external_module) + buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); + + if (strstarts(mod->name, "drivers/staging")) + buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); ++#endif + + if (strstarts(mod->name, "tools/testing")) + buf_printf(b, "\nMODULE_INFO(test, \"Y\");\n"); +@@ -1890,11 +1896,13 @@ static void add_depends(struct buffer *b + + static void add_srcversion(struct buffer *b, struct module *mod) + { ++#ifndef CONFIG_MODULE_STRIPPED + if (mod->srcversion[0]) { + buf_printf(b, "\n"); + buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n", + mod->srcversion); + } ++#endif + } + + static void write_buf(struct buffer *b, const char *fname) +@@ -1977,7 +1985,9 @@ static void write_mod_c_file(struct modu + add_exported_symbols(&buf, mod); + add_versions(&buf, mod); + add_depends(&buf, mod); ++#ifndef CONFIG_MODULE_STRIPPED + add_moddevtable(&buf, mod); ++#endif + add_srcversion(&buf, mod); + + ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name); diff --git a/target/linux/generic/hack-6.12/205-kconfig-abort-configuration-on-unset-symbol.patch b/target/linux/generic/hack-6.12/205-kconfig-abort-configuration-on-unset-symbol.patch new file mode 100644 index 00000000000..d7416d81a9e --- /dev/null +++ b/target/linux/generic/hack-6.12/205-kconfig-abort-configuration-on-unset-symbol.patch @@ -0,0 +1,41 @@ +From 310e8e04a05d9eb43fa9dd7f00143300afcaa37a Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Fri, 11 Nov 2022 13:33:44 +0100 +Subject: [PATCH] kconfig: abort configuration on unset symbol + +When a target configuration has unset Kconfig symbols, the build will +fail when OpenWrt is compiled with V=s and stdin is connected to a tty. + +In case OpenWrt is compiled without either of these preconditions, the +build will succeed with the symbols in question being unset. + +Modify the kernel configuration in a way it fails on unset symbols +regardless of the aforementioned preconditions. + +Signed-off-by: David Bauer +--- + scripts/kconfig/conf.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/scripts/kconfig/conf.c ++++ b/scripts/kconfig/conf.c +@@ -312,6 +312,9 @@ static int conf_askvalue(struct symbol * + } + /* fall through */ + default: ++ if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) { ++ exit(1); ++ } + fflush(stdout); + xfgets(line, sizeof(line), stdin); + break; +@@ -470,6 +473,9 @@ static void conf_choice(struct menu *men + } + /* fall through */ + case oldaskconfig: ++ if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) { ++ exit(1); ++ } + fflush(stdout); + xfgets(line, sizeof(line), stdin); + strip(line); diff --git a/target/linux/generic/hack-6.12/210-darwin_scripts_include.patch b/target/linux/generic/hack-6.12/210-darwin_scripts_include.patch new file mode 100644 index 00000000000..be59ca4fc92 --- /dev/null +++ b/target/linux/generic/hack-6.12/210-darwin_scripts_include.patch @@ -0,0 +1,3053 @@ +From db7c30dcd9a0391bf13b62c9f91e144d762ef43a Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +Date: Fri, 7 Jul 2017 17:00:49 +0200 +Subject: Add an OSX specific patch to make the kernel be compiled + +lede-commit: 3fc2a24f0422b2f55f9ed43f116db3111f700526 +Signed-off-by: Florian Fainelli +--- + scripts/kconfig/Makefile | 3 + + scripts/mod/elf.h | 3007 ++++++++++++++++++++++++++++++++++++++++++++ + scripts/mod/mk_elfconfig.c | 4 + + scripts/mod/modpost.h | 4 + + 4 files changed, 3018 insertions(+) + create mode 100644 scripts/mod/elf.h + +--- /dev/null ++++ b/scripts/mod/elf.h +@@ -0,0 +1,3007 @@ ++/* This file defines standard ELF types, structures, and macros. ++ Copyright (C) 1995-2012 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#ifndef _ELF_H ++#define _ELF_H 1 ++ ++/* Standard ELF types. */ ++ ++#include ++ ++/* Type for a 16-bit quantity. */ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++/* Types for signed and unsigned 32-bit quantities. */ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++/* Types for signed and unsigned 64-bit quantities. */ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++/* Type of addresses. */ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++/* Type of file offsets. */ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++/* Type for section indices, which are 16-bit quantities. */ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++/* Type for version symbol information. */ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++ ++/* The ELF file header. This appears at the start of every ELF file. */ ++ ++#define EI_NIDENT (16) ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf32_Half e_type; /* Object file type */ ++ Elf32_Half e_machine; /* Architecture */ ++ Elf32_Word e_version; /* Object file version */ ++ Elf32_Addr e_entry; /* Entry point virtual address */ ++ Elf32_Off e_phoff; /* Program header table file offset */ ++ Elf32_Off e_shoff; /* Section header table file offset */ ++ Elf32_Word e_flags; /* Processor-specific flags */ ++ Elf32_Half e_ehsize; /* ELF header size in bytes */ ++ Elf32_Half e_phentsize; /* Program header table entry size */ ++ Elf32_Half e_phnum; /* Program header table entry count */ ++ Elf32_Half e_shentsize; /* Section header table entry size */ ++ Elf32_Half e_shnum; /* Section header table entry count */ ++ Elf32_Half e_shstrndx; /* Section header string table index */ ++} Elf32_Ehdr; ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf64_Half e_type; /* Object file type */ ++ Elf64_Half e_machine; /* Architecture */ ++ Elf64_Word e_version; /* Object file version */ ++ Elf64_Addr e_entry; /* Entry point virtual address */ ++ Elf64_Off e_phoff; /* Program header table file offset */ ++ Elf64_Off e_shoff; /* Section header table file offset */ ++ Elf64_Word e_flags; /* Processor-specific flags */ ++ Elf64_Half e_ehsize; /* ELF header size in bytes */ ++ Elf64_Half e_phentsize; /* Program header table entry size */ ++ Elf64_Half e_phnum; /* Program header table entry count */ ++ Elf64_Half e_shentsize; /* Section header table entry size */ ++ Elf64_Half e_shnum; /* Section header table entry count */ ++ Elf64_Half e_shstrndx; /* Section header string table index */ ++} Elf64_Ehdr; ++ ++/* Fields in the e_ident array. The EI_* macros are indices into the ++ array. The macros under each EI_* macro are the values the byte ++ may have. */ ++ ++#define EI_MAG0 0 /* File identification byte 0 index */ ++#define ELFMAG0 0x7f /* Magic number byte 0 */ ++ ++#define EI_MAG1 1 /* File identification byte 1 index */ ++#define ELFMAG1 'E' /* Magic number byte 1 */ ++ ++#define EI_MAG2 2 /* File identification byte 2 index */ ++#define ELFMAG2 'L' /* Magic number byte 2 */ ++ ++#define EI_MAG3 3 /* File identification byte 3 index */ ++#define ELFMAG3 'F' /* Magic number byte 3 */ ++ ++/* Conglomeration of the identification bytes, for easy testing as a word. */ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 /* File class byte index */ ++#define ELFCLASSNONE 0 /* Invalid class */ ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATANONE 0 /* Invalid data encoding */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 /* File version byte index */ ++ /* Value must be EV_CURRENT */ ++ ++#define EI_OSABI 7 /* OS ABI identification */ ++#define ELFOSABI_NONE 0 /* UNIX System V ABI */ ++#define ELFOSABI_SYSV 0 /* Alias. */ ++#define ELFOSABI_HPUX 1 /* HP-UX */ ++#define ELFOSABI_NETBSD 2 /* NetBSD. */ ++#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ ++#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ ++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ ++#define ELFOSABI_AIX 7 /* IBM AIX. */ ++#define ELFOSABI_IRIX 8 /* SGI Irix. */ ++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ ++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ ++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ ++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ ++#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ ++#define ELFOSABI_ARM 97 /* ARM */ ++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ ++ ++#define EI_ABIVERSION 8 /* ABI version */ ++ ++#define EI_PAD 9 /* Byte index of padding bytes */ ++ ++/* Legal values for e_type (object file type). */ ++ ++#define ET_NONE 0 /* No file type */ ++#define ET_REL 1 /* Relocatable file */ ++#define ET_EXEC 2 /* Executable file */ ++#define ET_DYN 3 /* Shared object file */ ++#define ET_CORE 4 /* Core file */ ++#define ET_NUM 5 /* Number of defined types */ ++#define ET_LOOS 0xfe00 /* OS-specific range start */ ++#define ET_HIOS 0xfeff /* OS-specific range end */ ++#define ET_LOPROC 0xff00 /* Processor-specific range start */ ++#define ET_HIPROC 0xffff /* Processor-specific range end */ ++ ++/* Legal values for e_machine (architecture). */ ++ ++#define EM_NONE 0 /* No machine */ ++#define EM_M32 1 /* AT&T WE 32100 */ ++#define EM_SPARC 2 /* SUN SPARC */ ++#define EM_386 3 /* Intel 80386 */ ++#define EM_68K 4 /* Motorola m68k family */ ++#define EM_88K 5 /* Motorola m88k family */ ++#define EM_860 7 /* Intel 80860 */ ++#define EM_MIPS 8 /* MIPS R3000 big-endian */ ++#define EM_S370 9 /* IBM System/370 */ ++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ ++ ++#define EM_PARISC 15 /* HPPA */ ++#define EM_VPP500 17 /* Fujitsu VPP500 */ ++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ ++#define EM_960 19 /* Intel 80960 */ ++#define EM_PPC 20 /* PowerPC */ ++#define EM_PPC64 21 /* PowerPC 64-bit */ ++#define EM_S390 22 /* IBM S390 */ ++ ++#define EM_V800 36 /* NEC V800 series */ ++#define EM_FR20 37 /* Fujitsu FR20 */ ++#define EM_RH32 38 /* TRW RH-32 */ ++#define EM_RCE 39 /* Motorola RCE */ ++#define EM_ARM 40 /* ARM */ ++#define EM_FAKE_ALPHA 41 /* Digital Alpha */ ++#define EM_SH 42 /* Hitachi SH */ ++#define EM_SPARCV9 43 /* SPARC v9 64-bit */ ++#define EM_TRICORE 44 /* Siemens Tricore */ ++#define EM_ARC 45 /* Argonaut RISC Core */ ++#define EM_H8_300 46 /* Hitachi H8/300 */ ++#define EM_H8_300H 47 /* Hitachi H8/300H */ ++#define EM_H8S 48 /* Hitachi H8S */ ++#define EM_H8_500 49 /* Hitachi H8/500 */ ++#define EM_IA_64 50 /* Intel Merced */ ++#define EM_MIPS_X 51 /* Stanford MIPS-X */ ++#define EM_COLDFIRE 52 /* Motorola Coldfire */ ++#define EM_68HC12 53 /* Motorola M68HC12 */ ++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ ++#define EM_PCP 55 /* Siemens PCP */ ++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ ++#define EM_NDR1 57 /* Denso NDR1 microprocessor */ ++#define EM_STARCORE 58 /* Motorola Start*Core processor */ ++#define EM_ME16 59 /* Toyota ME16 processor */ ++#define EM_ST100 60 /* STMicroelectronic ST100 processor */ ++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ ++#define EM_X86_64 62 /* AMD x86-64 architecture */ ++#define EM_PDSP 63 /* Sony DSP Processor */ ++ ++#define EM_FX66 66 /* Siemens FX66 microcontroller */ ++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ ++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ ++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ ++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ ++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ ++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ ++#define EM_SVX 73 /* Silicon Graphics SVx */ ++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ ++#define EM_VAX 75 /* Digital VAX */ ++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ ++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ ++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ ++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ ++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ ++#define EM_HUANY 81 /* Harvard University machine-independent object files */ ++#define EM_PRISM 82 /* SiTera Prism */ ++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ ++#define EM_FR30 84 /* Fujitsu FR30 */ ++#define EM_D10V 85 /* Mitsubishi D10V */ ++#define EM_D30V 86 /* Mitsubishi D30V */ ++#define EM_V850 87 /* NEC v850 */ ++#define EM_M32R 88 /* Mitsubishi M32R */ ++#define EM_MN10300 89 /* Matsushita MN10300 */ ++#define EM_MN10200 90 /* Matsushita MN10200 */ ++#define EM_PJ 91 /* picoJava */ ++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ ++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ ++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ ++#define EM_TILEPRO 188 /* Tilera TILEPro */ ++#define EM_TILEGX 191 /* Tilera TILE-Gx */ ++#define EM_NUM 192 ++ ++/* If it is necessary to assign new unofficial EM_* values, please ++ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the ++ chances of collision with official or non-GNU unofficial values. */ ++ ++#define EM_ALPHA 0x9026 ++ ++/* Legal values for e_version (version). */ ++ ++#define EV_NONE 0 /* Invalid ELF version */ ++#define EV_CURRENT 1 /* Current version */ ++#define EV_NUM 2 ++ ++/* Section header. */ ++ ++typedef struct ++{ ++ Elf32_Word sh_name; /* Section name (string tbl index) */ ++ Elf32_Word sh_type; /* Section type */ ++ Elf32_Word sh_flags; /* Section flags */ ++ Elf32_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf32_Off sh_offset; /* Section file offset */ ++ Elf32_Word sh_size; /* Section size in bytes */ ++ Elf32_Word sh_link; /* Link to another section */ ++ Elf32_Word sh_info; /* Additional section information */ ++ Elf32_Word sh_addralign; /* Section alignment */ ++ Elf32_Word sh_entsize; /* Entry size if section holds table */ ++} Elf32_Shdr; ++ ++typedef struct ++{ ++ Elf64_Word sh_name; /* Section name (string tbl index) */ ++ Elf64_Word sh_type; /* Section type */ ++ Elf64_Xword sh_flags; /* Section flags */ ++ Elf64_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf64_Off sh_offset; /* Section file offset */ ++ Elf64_Xword sh_size; /* Section size in bytes */ ++ Elf64_Word sh_link; /* Link to another section */ ++ Elf64_Word sh_info; /* Additional section information */ ++ Elf64_Xword sh_addralign; /* Section alignment */ ++ Elf64_Xword sh_entsize; /* Entry size if section holds table */ ++} Elf64_Shdr; ++ ++/* Special section indices. */ ++ ++#define SHN_UNDEF 0 /* Undefined section */ ++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ ++#define SHN_LOPROC 0xff00 /* Start of processor-specific */ ++#define SHN_BEFORE 0xff00 /* Order section before all others ++ (Solaris). */ ++#define SHN_AFTER 0xff01 /* Order section after all others ++ (Solaris). */ ++#define SHN_HIPROC 0xff1f /* End of processor-specific */ ++#define SHN_LOOS 0xff20 /* Start of OS-specific */ ++#define SHN_HIOS 0xff3f /* End of OS-specific */ ++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ ++#define SHN_COMMON 0xfff2 /* Associated symbol is common */ ++#define SHN_XINDEX 0xffff /* Index is in extra table. */ ++#define SHN_HIRESERVE 0xffff /* End of reserved indices */ ++ ++/* Legal values for sh_type (section type). */ ++ ++#define SHT_NULL 0 /* Section header table entry unused */ ++#define SHT_PROGBITS 1 /* Program data */ ++#define SHT_SYMTAB 2 /* Symbol table */ ++#define SHT_STRTAB 3 /* String table */ ++#define SHT_RELA 4 /* Relocation entries with addends */ ++#define SHT_HASH 5 /* Symbol hash table */ ++#define SHT_DYNAMIC 6 /* Dynamic linking information */ ++#define SHT_NOTE 7 /* Notes */ ++#define SHT_NOBITS 8 /* Program space with no data (bss) */ ++#define SHT_REL 9 /* Relocation entries, no addends */ ++#define SHT_SHLIB 10 /* Reserved */ ++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ ++#define SHT_INIT_ARRAY 14 /* Array of constructors */ ++#define SHT_FINI_ARRAY 15 /* Array of destructors */ ++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ ++#define SHT_GROUP 17 /* Section group */ ++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ ++#define SHT_NUM 19 /* Number of defined types. */ ++#define SHT_LOOS 0x60000000 /* Start OS-specific. */ ++#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ ++#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ ++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ ++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ ++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ ++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ ++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ ++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ ++#define SHT_HIOS 0x6fffffff /* End OS-specific type */ ++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define SHT_LOUSER 0x80000000 /* Start of application-specific */ ++#define SHT_HIUSER 0x8fffffff /* End of application-specific */ ++ ++/* Legal values for sh_flags (section flags). */ ++ ++#define SHF_WRITE (1 << 0) /* Writable */ ++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ ++#define SHF_EXECINSTR (1 << 2) /* Executable */ ++#define SHF_MERGE (1 << 4) /* Might be merged */ ++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ ++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ ++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ ++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling ++ required */ ++#define SHF_GROUP (1 << 9) /* Section is member of a group. */ ++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ ++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ ++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ ++#define SHF_ORDERED (1 << 30) /* Special ordering requirement ++ (Solaris). */ ++#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless ++ referenced or allocated (Solaris).*/ ++ ++/* Section group handling. */ ++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ ++ ++/* Symbol table entry. */ ++ ++typedef struct ++{ ++ Elf32_Word st_name; /* Symbol name (string tbl index) */ ++ Elf32_Addr st_value; /* Symbol value */ ++ Elf32_Word st_size; /* Symbol size */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf32_Section st_shndx; /* Section index */ ++} Elf32_Sym; ++ ++typedef struct ++{ ++ Elf64_Word st_name; /* Symbol name (string tbl index) */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf64_Section st_shndx; /* Section index */ ++ Elf64_Addr st_value; /* Symbol value */ ++ Elf64_Xword st_size; /* Symbol size */ ++} Elf64_Sym; ++ ++/* The syminfo section if available contains additional information about ++ every dynamic symbol. */ ++ ++typedef struct ++{ ++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf32_Half si_flags; /* Per symbol flags */ ++} Elf32_Syminfo; ++ ++typedef struct ++{ ++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf64_Half si_flags; /* Per symbol flags */ ++} Elf64_Syminfo; ++ ++/* Possible values for si_boundto. */ ++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ ++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ ++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ ++ ++/* Possible bitmasks for si_flags. */ ++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ ++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ ++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ ++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy ++ loaded */ ++/* Syminfo version values. */ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++ ++/* How to extract and insert information held in the st_info field. */ ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++/* Legal values for ST_BIND subfield of st_info (symbol binding). */ ++ ++#define STB_LOCAL 0 /* Local symbol */ ++#define STB_GLOBAL 1 /* Global symbol */ ++#define STB_WEAK 2 /* Weak symbol */ ++#define STB_NUM 3 /* Number of defined types. */ ++#define STB_LOOS 10 /* Start of OS-specific */ ++#define STB_GNU_UNIQUE 10 /* Unique symbol. */ ++#define STB_HIOS 12 /* End of OS-specific */ ++#define STB_LOPROC 13 /* Start of processor-specific */ ++#define STB_HIPROC 15 /* End of processor-specific */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_NOTYPE 0 /* Symbol type is unspecified */ ++#define STT_OBJECT 1 /* Symbol is a data object */ ++#define STT_FUNC 2 /* Symbol is a code object */ ++#define STT_SECTION 3 /* Symbol associated with a section */ ++#define STT_FILE 4 /* Symbol's name is file name */ ++#define STT_COMMON 5 /* Symbol is a common data object */ ++#define STT_TLS 6 /* Symbol is thread-local data object*/ ++#define STT_NUM 7 /* Number of defined types. */ ++#define STT_LOOS 10 /* Start of OS-specific */ ++#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ ++#define STT_HIOS 12 /* End of OS-specific */ ++#define STT_LOPROC 13 /* Start of processor-specific */ ++#define STT_HIPROC 15 /* End of processor-specific */ ++ ++ ++/* Symbol table indices are found in the hash buckets and chain table ++ of a symbol hash table section. This special index value indicates ++ the end of a chain, meaning no further symbols are found in that bucket. */ ++ ++#define STN_UNDEF 0 /* End of a chain. */ ++ ++ ++/* How to extract and insert information held in the st_other field. */ ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++ ++/* For ELF64 the definitions are the same. */ ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++/* Symbol visibility specification encoded in the st_other field. */ ++#define STV_DEFAULT 0 /* Default symbol visibility rules */ ++#define STV_INTERNAL 1 /* Processor specific hidden class */ ++#define STV_HIDDEN 2 /* Sym unavailable in other modules */ ++#define STV_PROTECTED 3 /* Not preemptible, not exported */ ++ ++ ++/* Relocation table entry without addend (in section of type SHT_REL). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++} Elf32_Rel; ++ ++/* I have seen two different definitions of the Elf64_Rel and ++ Elf64_Rela structures, so we'll leave them out until Novell (or ++ whoever) gets their act together. */ ++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++} Elf64_Rel; ++ ++/* Relocation table entry with addend (in section of type SHT_RELA). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++ Elf32_Sword r_addend; /* Addend */ ++} Elf32_Rela; ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++ Elf64_Sxword r_addend; /* Addend */ ++} Elf64_Rela; ++ ++/* How to extract and insert information held in the r_info field. */ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++/* Program segment header. */ ++ ++typedef struct ++{ ++ Elf32_Word p_type; /* Segment type */ ++ Elf32_Off p_offset; /* Segment file offset */ ++ Elf32_Addr p_vaddr; /* Segment virtual address */ ++ Elf32_Addr p_paddr; /* Segment physical address */ ++ Elf32_Word p_filesz; /* Segment size in file */ ++ Elf32_Word p_memsz; /* Segment size in memory */ ++ Elf32_Word p_flags; /* Segment flags */ ++ Elf32_Word p_align; /* Segment alignment */ ++} Elf32_Phdr; ++ ++typedef struct ++{ ++ Elf64_Word p_type; /* Segment type */ ++ Elf64_Word p_flags; /* Segment flags */ ++ Elf64_Off p_offset; /* Segment file offset */ ++ Elf64_Addr p_vaddr; /* Segment virtual address */ ++ Elf64_Addr p_paddr; /* Segment physical address */ ++ Elf64_Xword p_filesz; /* Segment size in file */ ++ Elf64_Xword p_memsz; /* Segment size in memory */ ++ Elf64_Xword p_align; /* Segment alignment */ ++} Elf64_Phdr; ++ ++/* Special value for e_phnum. This indicates that the real number of ++ program headers is too large to fit into e_phnum. Instead the real ++ value is in the field sh_info of section 0. */ ++ ++#define PN_XNUM 0xffff ++ ++/* Legal values for p_type (segment type). */ ++ ++#define PT_NULL 0 /* Program header table entry unused */ ++#define PT_LOAD 1 /* Loadable program segment */ ++#define PT_DYNAMIC 2 /* Dynamic linking information */ ++#define PT_INTERP 3 /* Program interpreter */ ++#define PT_NOTE 4 /* Auxiliary information */ ++#define PT_SHLIB 5 /* Reserved */ ++#define PT_PHDR 6 /* Entry for header table itself */ ++#define PT_TLS 7 /* Thread-local storage segment */ ++#define PT_NUM 8 /* Number of defined types */ ++#define PT_LOOS 0x60000000 /* Start of OS-specific */ ++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ ++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ ++#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ ++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff /* End of OS-specific */ ++#define PT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define PT_HIPROC 0x7fffffff /* End of processor-specific */ ++ ++/* Legal values for p_flags (segment flags). */ ++ ++#define PF_X (1 << 0) /* Segment is executable */ ++#define PF_W (1 << 1) /* Segment is writable */ ++#define PF_R (1 << 2) /* Segment is readable */ ++#define PF_MASKOS 0x0ff00000 /* OS-specific */ ++#define PF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Legal values for note segment descriptor types for core files. */ ++ ++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ ++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ ++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ ++#define NT_PRXREG 4 /* Contains copy of prxregset struct */ ++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ ++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ ++#define NT_AUXV 6 /* Contains copy of auxv array */ ++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ ++#define NT_ASRS 8 /* Contains copy of asrset struct */ ++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ ++#define NT_PSINFO 13 /* Contains copy of psinfo struct */ ++#define NT_PRCRED 14 /* Contains copy of prcred struct */ ++#define NT_UTSNAME 15 /* Contains copy of utsname struct */ ++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ ++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ ++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ ++#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ ++#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ ++#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ ++#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ ++#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ ++#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ ++#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ ++ ++/* Legal values for the note segment descriptor types for object files. */ ++ ++#define NT_VERSION 1 /* Contains a version string. */ ++ ++ ++/* Dynamic section entry. */ ++ ++typedef struct ++{ ++ Elf32_Sword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf32_Word d_val; /* Integer value */ ++ Elf32_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct ++{ ++ Elf64_Sxword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf64_Xword d_val; /* Integer value */ ++ Elf64_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf64_Dyn; ++ ++/* Legal values for d_tag (dynamic entry type). */ ++ ++#define DT_NULL 0 /* Marks end of dynamic section */ ++#define DT_NEEDED 1 /* Name of needed library */ ++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ ++#define DT_PLTGOT 3 /* Processor defined value */ ++#define DT_HASH 4 /* Address of symbol hash table */ ++#define DT_STRTAB 5 /* Address of string table */ ++#define DT_SYMTAB 6 /* Address of symbol table */ ++#define DT_RELA 7 /* Address of Rela relocs */ ++#define DT_RELASZ 8 /* Total size of Rela relocs */ ++#define DT_RELAENT 9 /* Size of one Rela reloc */ ++#define DT_STRSZ 10 /* Size of string table */ ++#define DT_SYMENT 11 /* Size of one symbol table entry */ ++#define DT_INIT 12 /* Address of init function */ ++#define DT_FINI 13 /* Address of termination function */ ++#define DT_SONAME 14 /* Name of shared object */ ++#define DT_RPATH 15 /* Library search path (deprecated) */ ++#define DT_SYMBOLIC 16 /* Start symbol search here */ ++#define DT_REL 17 /* Address of Rel relocs */ ++#define DT_RELSZ 18 /* Total size of Rel relocs */ ++#define DT_RELENT 19 /* Size of one Rel reloc */ ++#define DT_PLTREL 20 /* Type of reloc in PLT */ ++#define DT_DEBUG 21 /* For debugging; unspecified */ ++#define DT_TEXTREL 22 /* Reloc might modify .text */ ++#define DT_JMPREL 23 /* Address of PLT relocs */ ++#define DT_BIND_NOW 24 /* Process relocations of object */ ++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ ++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ ++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ ++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ ++#define DT_RUNPATH 29 /* Library search path */ ++#define DT_FLAGS 30 /* Flags for the object being loaded */ ++#define DT_ENCODING 32 /* Start of encoded range */ ++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ ++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ ++#define DT_NUM 34 /* Number used */ ++#define DT_LOOS 0x6000000d /* Start of OS-specific */ ++#define DT_HIOS 0x6ffff000 /* End of OS-specific */ ++#define DT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define DT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ ++ ++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the ++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's ++ approach. */ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ ++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting ++ the following DT_* entry. */ ++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ ++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ ++#define DT_VALNUM 12 ++ ++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the ++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. ++ ++ If any adjustment is made to the ELF object after it has been ++ built these entries will need to be adjusted. */ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ ++#define DT_TLSDESC_PLT 0x6ffffef6 ++#define DT_TLSDESC_GOT 0x6ffffef7 ++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ ++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ ++#define DT_CONFIG 0x6ffffefa /* Configuration information. */ ++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ ++#define DT_AUDIT 0x6ffffefc /* Object auditing. */ ++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ ++#define DT_MOVETAB 0x6ffffefe /* Move table. */ ++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ ++#define DT_ADDRNUM 11 ++ ++/* The versioning entry types. The next are defined as part of the ++ GNU extension. */ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++/* These were chosen by Sun. */ ++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ ++#define DT_VERDEF 0x6ffffffc /* Address of version definition ++ table */ ++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ ++#define DT_VERNEED 0x6ffffffe /* Address of table with needed ++ versions */ ++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ ++#define DT_VERSIONTAGNUM 16 ++ ++/* Sun added these machine-independent extensions in the "processor-specific" ++ range. Be compatible. */ ++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ ++#define DT_FILTER 0x7fffffff /* Shared object to get values from */ ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++/* Values of `d_un.d_val' in the DT_FLAGS entry. */ ++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ ++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ ++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ ++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ ++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ ++ ++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 ++ entry in the dynamic section. */ ++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ ++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ ++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ ++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ ++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ ++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ ++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ ++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ ++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ ++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ ++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ ++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ ++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ ++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ ++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ ++ ++/* Flags for the feature selection in DT_FEATURE_1. */ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ ++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ ++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not ++ generally available. */ ++ ++/* Version definition sections. */ ++ ++typedef struct ++{ ++ Elf32_Half vd_version; /* Version revision */ ++ Elf32_Half vd_flags; /* Version information */ ++ Elf32_Half vd_ndx; /* Version Index */ ++ Elf32_Half vd_cnt; /* Number of associated aux entries */ ++ Elf32_Word vd_hash; /* Version name hash value */ ++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf32_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf32_Verdef; ++ ++typedef struct ++{ ++ Elf64_Half vd_version; /* Version revision */ ++ Elf64_Half vd_flags; /* Version information */ ++ Elf64_Half vd_ndx; /* Version Index */ ++ Elf64_Half vd_cnt; /* Number of associated aux entries */ ++ Elf64_Word vd_hash; /* Version name hash value */ ++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf64_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf64_Verdef; ++ ++ ++/* Legal values for vd_version (version revision). */ ++#define VER_DEF_NONE 0 /* No version */ ++#define VER_DEF_CURRENT 1 /* Current version */ ++#define VER_DEF_NUM 2 /* Given version number */ ++ ++/* Legal values for vd_flags (version information flags). */ ++#define VER_FLG_BASE 0x1 /* Version definition of file itself */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++/* Versym symbol index values. */ ++#define VER_NDX_LOCAL 0 /* Symbol is local. */ ++#define VER_NDX_GLOBAL 1 /* Symbol is global. */ ++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ ++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ ++ ++/* Auxialiary version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vda_name; /* Version or dependency names */ ++ Elf32_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf32_Verdaux; ++ ++typedef struct ++{ ++ Elf64_Word vda_name; /* Version or dependency names */ ++ Elf64_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf64_Verdaux; ++ ++ ++/* Version dependency section. */ ++ ++typedef struct ++{ ++ Elf32_Half vn_version; /* Version of structure */ ++ Elf32_Half vn_cnt; /* Number of associated aux entries */ ++ Elf32_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf32_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf32_Verneed; ++ ++typedef struct ++{ ++ Elf64_Half vn_version; /* Version of structure */ ++ Elf64_Half vn_cnt; /* Number of associated aux entries */ ++ Elf64_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf64_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf64_Verneed; ++ ++ ++/* Legal values for vn_version (version revision). */ ++#define VER_NEED_NONE 0 /* No version */ ++#define VER_NEED_CURRENT 1 /* Current version */ ++#define VER_NEED_NUM 2 /* Given version number */ ++ ++/* Auxiliary needed version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vna_hash; /* Hash value of dependency name */ ++ Elf32_Half vna_flags; /* Dependency specific information */ ++ Elf32_Half vna_other; /* Unused */ ++ Elf32_Word vna_name; /* Dependency name string offset */ ++ Elf32_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf32_Vernaux; ++ ++typedef struct ++{ ++ Elf64_Word vna_hash; /* Hash value of dependency name */ ++ Elf64_Half vna_flags; /* Dependency specific information */ ++ Elf64_Half vna_other; /* Unused */ ++ Elf64_Word vna_name; /* Dependency name string offset */ ++ Elf64_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf64_Vernaux; ++ ++ ++/* Legal values for vna_flags. */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++ ++/* Auxiliary vector. */ ++ ++/* This vector is normally only used by the program interpreter. The ++ usual definition in an ABI supplement uses the name auxv_t. The ++ vector is not usually defined in a standard file, but it ++ can't hurt. We rename it to avoid conflicts. The sizes of these ++ types are an arrangement between the exec server and the program ++ interpreter, so we don't fully specify them here. */ ++ ++typedef struct ++{ ++ uint32_t a_type; /* Entry type */ ++ union ++ { ++ uint32_t a_val; /* Integer value */ ++ /* We use to have pointer elements added here. We cannot do that, ++ though, since it does not work when using 32-bit definitions ++ on 64-bit platforms and vice versa. */ ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct ++{ ++ uint64_t a_type; /* Entry type */ ++ union ++ { ++ uint64_t a_val; /* Integer value */ ++ /* We use to have pointer elements added here. We cannot do that, ++ though, since it does not work when using 32-bit definitions ++ on 64-bit platforms and vice versa. */ ++ } a_un; ++} Elf64_auxv_t; ++ ++/* Legal values for a_type (entry type). */ ++ ++#define AT_NULL 0 /* End of vector */ ++#define AT_IGNORE 1 /* Entry should be ignored */ ++#define AT_EXECFD 2 /* File descriptor of program */ ++#define AT_PHDR 3 /* Program headers for program */ ++#define AT_PHENT 4 /* Size of program header entry */ ++#define AT_PHNUM 5 /* Number of program headers */ ++#define AT_PAGESZ 6 /* System page size */ ++#define AT_BASE 7 /* Base address of interpreter */ ++#define AT_FLAGS 8 /* Flags */ ++#define AT_ENTRY 9 /* Entry point of program */ ++#define AT_NOTELF 10 /* Program is not ELF */ ++#define AT_UID 11 /* Real uid */ ++#define AT_EUID 12 /* Effective uid */ ++#define AT_GID 13 /* Real gid */ ++#define AT_EGID 14 /* Effective gid */ ++#define AT_CLKTCK 17 /* Frequency of times() */ ++ ++/* Some more special a_type values describing the hardware. */ ++#define AT_PLATFORM 15 /* String identifying platform. */ ++#define AT_HWCAP 16 /* Machine dependent hints about ++ processor capabilities. */ ++ ++/* This entry gives some information about the FPU initialization ++ performed by the kernel. */ ++#define AT_FPUCW 18 /* Used FPU control word. */ ++ ++/* Cache block sizes. */ ++#define AT_DCACHEBSIZE 19 /* Data cache block size. */ ++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ ++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ ++ ++/* A special ignored value for PPC, used by the kernel to control the ++ interpretation of the AUXV. Must be > 16. */ ++#define AT_IGNOREPPC 22 /* Entry should be ignored. */ ++ ++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ ++ ++#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ ++ ++#define AT_RANDOM 25 /* Address of 16 random bytes. */ ++ ++#define AT_EXECFN 31 /* Filename of executable. */ ++ ++/* Pointer to the global system page used for system calls and other ++ nice things. */ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains ++ log2 of line size; mask those to get cache size. */ ++#define AT_L1I_CACHESHAPE 34 ++#define AT_L1D_CACHESHAPE 35 ++#define AT_L2_CACHESHAPE 36 ++#define AT_L3_CACHESHAPE 37 ++ ++/* Note section contents. Each entry in the note section begins with ++ a header of a fixed form. */ ++ ++typedef struct ++{ ++ Elf32_Word n_namesz; /* Length of the note's name. */ ++ Elf32_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf32_Word n_type; /* Type of the note. */ ++} Elf32_Nhdr; ++ ++typedef struct ++{ ++ Elf64_Word n_namesz; /* Length of the note's name. */ ++ Elf64_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf64_Word n_type; /* Type of the note. */ ++} Elf64_Nhdr; ++ ++/* Known names of notes. */ ++ ++/* Solaris entries in the note section have this name. */ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++/* Note entries for GNU systems have this name. */ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++/* Defined types of notes for Solaris. */ ++ ++/* Value of descriptor (one word) is desired pagesize for the binary. */ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++/* Defined note types for GNU systems. */ ++ ++/* ABI information. The descriptor consists of words: ++ word 0: OS descriptor ++ word 1: major version of the ABI ++ word 2: minor version of the ABI ++ word 3: subminor version of the ABI ++*/ ++#define NT_GNU_ABI_TAG 1 ++#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ ++ ++/* Known OSes. These values can appear in word 0 of an ++ NT_GNU_ABI_TAG note section entry. */ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++/* Synthetic hwcap information. The descriptor begins with two words: ++ word 0: number of entries ++ word 1: bitmask of enabled entries ++ Then follow variable-length entries, one byte followed by a ++ '\0'-terminated hwcap name string. The byte gives the bit ++ number to test if enabled, (1U << bit) & bitmask. */ ++#define NT_GNU_HWCAP 2 ++ ++/* Build ID bits as generated by ld --build-id. ++ The descriptor consists of any nonzero number of bytes. */ ++#define NT_GNU_BUILD_ID 3 ++ ++/* Version note generated by GNU gold containing a version string. */ ++#define NT_GNU_GOLD_VERSION 4 ++ ++ ++/* Move records. */ ++typedef struct ++{ ++ Elf32_Xword m_value; /* Symbol value. */ ++ Elf32_Word m_info; /* Size and index. */ ++ Elf32_Word m_poffset; /* Symbol offset. */ ++ Elf32_Half m_repeat; /* Repeat count. */ ++ Elf32_Half m_stride; /* Stride info. */ ++} Elf32_Move; ++ ++typedef struct ++{ ++ Elf64_Xword m_value; /* Symbol value. */ ++ Elf64_Xword m_info; /* Size and index. */ ++ Elf64_Xword m_poffset; /* Symbol offset. */ ++ Elf64_Half m_repeat; /* Repeat count. */ ++ Elf64_Half m_stride; /* Stride info. */ ++} Elf64_Move; ++ ++/* Macro to construct move records. */ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++ ++/* Motorola 68k specific definitions. */ ++ ++/* Values for Elf32_Ehdr.e_flags. */ ++#define EF_CPU32 0x00810000 ++ ++/* m68k relocs. */ ++ ++#define R_68K_NONE 0 /* No reloc */ ++#define R_68K_32 1 /* Direct 32 bit */ ++#define R_68K_16 2 /* Direct 16 bit */ ++#define R_68K_8 3 /* Direct 8 bit */ ++#define R_68K_PC32 4 /* PC relative 32 bit */ ++#define R_68K_PC16 5 /* PC relative 16 bit */ ++#define R_68K_PC8 6 /* PC relative 8 bit */ ++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ ++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ ++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ ++#define R_68K_GOT32O 10 /* 32 bit GOT offset */ ++#define R_68K_GOT16O 11 /* 16 bit GOT offset */ ++#define R_68K_GOT8O 12 /* 8 bit GOT offset */ ++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ ++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ ++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ ++#define R_68K_PLT32O 16 /* 32 bit PLT offset */ ++#define R_68K_PLT16O 17 /* 16 bit PLT offset */ ++#define R_68K_PLT8O 18 /* 8 bit PLT offset */ ++#define R_68K_COPY 19 /* Copy symbol at runtime */ ++#define R_68K_GLOB_DAT 20 /* Create GOT entry */ ++#define R_68K_JMP_SLOT 21 /* Create PLT entry */ ++#define R_68K_RELATIVE 22 /* Adjust by program base */ ++#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ ++#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ ++#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ ++#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ ++#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ ++#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ ++#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ ++#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ ++#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ ++#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ ++#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ ++#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ ++#define R_68K_TLS_LE32 37 /* 32 bit offset relative to ++ static TLS block */ ++#define R_68K_TLS_LE16 38 /* 16 bit offset relative to ++ static TLS block */ ++#define R_68K_TLS_LE8 39 /* 8 bit offset relative to ++ static TLS block */ ++#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ ++#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ ++#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ ++/* Keep this the last entry. */ ++#define R_68K_NUM 43 ++ ++/* Intel 80386 specific definitions. */ ++ ++/* i386 relocs. */ ++ ++#define R_386_NONE 0 /* No reloc */ ++#define R_386_32 1 /* Direct 32 bit */ ++#define R_386_PC32 2 /* PC relative 32 bit */ ++#define R_386_GOT32 3 /* 32 bit GOT entry */ ++#define R_386_PLT32 4 /* 32 bit PLT address */ ++#define R_386_COPY 5 /* Copy symbol at runtime */ ++#define R_386_GLOB_DAT 6 /* Create GOT entry */ ++#define R_386_JMP_SLOT 7 /* Create PLT entry */ ++#define R_386_RELATIVE 8 /* Adjust by program base */ ++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ ++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ ++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS ++ block offset */ ++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block ++ offset */ ++#define R_386_TLS_LE 17 /* Offset relative to static TLS ++ block */ ++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of ++ general dynamic thread local data */ ++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of ++ local dynamic thread local data ++ in LE code */ ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic ++ thread local data */ ++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ ++#define R_386_TLS_GD_CALL 26 /* Relocation for call to ++ __tls_get_addr() */ ++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ ++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic ++ thread local data in LE code */ ++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ ++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to ++ __tls_get_addr() in LDM code */ ++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ ++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ ++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS ++ block offset */ ++#define R_386_TLS_LE_32 34 /* Negated offset relative to static ++ TLS block */ ++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ ++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ ++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ ++/* 38? */ ++#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ ++#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS ++ descriptor for ++ relaxation. */ ++#define R_386_TLS_DESC 41 /* TLS descriptor containing ++ pointer to code and to ++ argument, returning the TLS ++ offset for the symbol. */ ++#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ ++/* Keep this the last entry. */ ++#define R_386_NUM 43 ++ ++/* SUN SPARC specific definitions. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ ++ ++/* Values for Elf64_Ehdr.e_flags. */ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 /* little endian data */ ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ ++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ ++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ ++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ ++ ++/* SPARC relocs. */ ++ ++#define R_SPARC_NONE 0 /* No reloc */ ++#define R_SPARC_8 1 /* Direct 8 bit */ ++#define R_SPARC_16 2 /* Direct 16 bit */ ++#define R_SPARC_32 3 /* Direct 32 bit */ ++#define R_SPARC_DISP8 4 /* PC relative 8 bit */ ++#define R_SPARC_DISP16 5 /* PC relative 16 bit */ ++#define R_SPARC_DISP32 6 /* PC relative 32 bit */ ++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ ++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ ++#define R_SPARC_HI22 9 /* High 22 bit */ ++#define R_SPARC_22 10 /* Direct 22 bit */ ++#define R_SPARC_13 11 /* Direct 13 bit */ ++#define R_SPARC_LO10 12 /* Truncated 10 bit */ ++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ ++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ ++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ ++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ ++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ ++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ ++#define R_SPARC_COPY 19 /* Copy symbol at runtime */ ++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ ++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ ++#define R_SPARC_RELATIVE 22 /* Adjust by program base */ ++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ ++ ++/* Additional Sparc64 relocs. */ ++ ++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ ++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ ++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ ++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ ++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ ++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ ++#define R_SPARC_10 30 /* Direct 10 bit */ ++#define R_SPARC_11 31 /* Direct 11 bit */ ++#define R_SPARC_64 32 /* Direct 64 bit */ ++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ ++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ ++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ ++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ ++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ ++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ ++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ ++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ ++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ ++#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */ ++#define R_SPARC_7 43 /* Direct 7 bit */ ++#define R_SPARC_5 44 /* Direct 5 bit */ ++#define R_SPARC_6 45 /* Direct 6 bit */ ++#define R_SPARC_DISP64 46 /* PC relative 64 bit */ ++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ ++#define R_SPARC_HIX22 48 /* High 22 bit complemented */ ++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ ++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ ++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ ++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ ++#define R_SPARC_REGISTER 53 /* Global register usage */ ++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ ++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++#define R_SPARC_GOTDATA_HIX22 80 ++#define R_SPARC_GOTDATA_LOX10 81 ++#define R_SPARC_GOTDATA_OP_HIX22 82 ++#define R_SPARC_GOTDATA_OP_LOX10 83 ++#define R_SPARC_GOTDATA_OP 84 ++#define R_SPARC_H34 85 ++#define R_SPARC_SIZE32 86 ++#define R_SPARC_SIZE64 87 ++#define R_SPARC_WDISP10 88 ++#define R_SPARC_JMP_IREL 248 ++#define R_SPARC_IRELATIVE 249 ++#define R_SPARC_GNU_VTINHERIT 250 ++#define R_SPARC_GNU_VTENTRY 251 ++#define R_SPARC_REV32 252 ++/* Keep this the last entry. */ ++#define R_SPARC_NUM 253 ++ ++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++/* MIPS R3000 specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ ++#define EF_MIPS_PIC 2 /* Contains PIC code */ ++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ ++ ++/* Legal values for MIPS architecture level. */ ++ ++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* The following are non-official names and should not be used. */ ++ ++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* Special section indices. */ ++ ++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ ++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ ++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ ++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ ++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ ++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ ++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ ++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ ++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++/* Symbol tables. */ ++ ++/* MIPS specific values for `st_other'. */ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_PLT 0x8 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++/* MIPS specific values for `st_info'. */ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++/* Entries found in sections of type SHT_MIPS_GPTAB. */ ++ ++typedef union ++{ ++ struct ++ { ++ Elf32_Word gt_current_g_value; /* -G value used for compilation */ ++ Elf32_Word gt_unused; /* Not used */ ++ } gt_header; /* First entry in section */ ++ struct ++ { ++ Elf32_Word gt_g_value; /* If this value were used for -G */ ++ Elf32_Word gt_bytes; /* This many bytes would be used */ ++ } gt_entry; /* Subsequent entries in section */ ++} Elf32_gptab; ++ ++/* Entry found in sections of type SHT_MIPS_REGINFO. */ ++ ++typedef struct ++{ ++ Elf32_Word ri_gprmask; /* General registers used */ ++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ ++ Elf32_Sword ri_gp_value; /* $gp register value */ ++} Elf32_RegInfo; ++ ++/* Entries found in sections of type SHT_MIPS_OPTIONS. */ ++ ++typedef struct ++{ ++ unsigned char kind; /* Determines interpretation of the ++ variable part of descriptor. */ ++ unsigned char size; /* Size of descriptor, including header. */ ++ Elf32_Section section; /* Section header index of section affected, ++ 0 for global options. */ ++ Elf32_Word info; /* Kind-specific information. */ ++} Elf_Options; ++ ++/* Values for `kind' field in Elf_Options. */ ++ ++#define ODK_NULL 0 /* Undefined. */ ++#define ODK_REGINFO 1 /* Register usage information. */ ++#define ODK_EXCEPTIONS 2 /* Exception processing options. */ ++#define ODK_PAD 3 /* Section padding options. */ ++#define ODK_HWPATCH 4 /* Hardware workarounds performed */ ++#define ODK_FILL 5 /* record the fill value used by the linker. */ ++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ ++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ ++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ ++ ++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ ++ ++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ ++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ ++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ ++#define OEX_SMM 0x20000 /* Force sequential memory mode? */ ++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ ++ ++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ ++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ ++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ ++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++/* Entry found in `.options' section. */ ++ ++typedef struct ++{ ++ Elf32_Word hwp_flags1; /* Extra flags. */ ++ Elf32_Word hwp_flags2; /* Extra flags. */ ++} Elf_Options_Hw; ++ ++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++/* MIPS relocs. */ ++ ++#define R_MIPS_NONE 0 /* No reloc */ ++#define R_MIPS_16 1 /* Direct 16 bit */ ++#define R_MIPS_32 2 /* Direct 32 bit */ ++#define R_MIPS_REL32 3 /* PC relative 32 bit */ ++#define R_MIPS_26 4 /* Direct 26 bit shifted */ ++#define R_MIPS_HI16 5 /* High 16 bit */ ++#define R_MIPS_LO16 6 /* Low 16 bit */ ++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ ++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ ++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ ++#define R_MIPS_PC16 10 /* PC relative 16 bit */ ++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ ++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ ++#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ ++#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ ++#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ ++#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ ++#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ ++#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ ++#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ ++#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ ++#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ ++#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ ++#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ ++#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ ++#define R_MIPS_GLOB_DAT 51 ++#define R_MIPS_COPY 126 ++#define R_MIPS_JUMP_SLOT 127 ++/* Keep this the last entry. */ ++#define R_MIPS_NUM 128 ++ ++/* Legal values for p_type field of Elf32_Phdr. */ ++ ++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ ++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++/* Special program header types. */ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++/* Legal values for d_tag field of Elf32_Dyn. */ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ ++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ ++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ ++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ ++#define DT_MIPS_FLAGS 0x70000005 /* Flags */ ++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ ++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ ++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ ++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ ++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ ++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ ++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ ++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ ++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ ++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in ++ DT_MIPS_DELTA_CLASS. */ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in ++ DT_MIPS_DELTA_INSTANCE. */ ++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in ++ DT_MIPS_DELTA_RELOC. */ ++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta ++ relocations refer to. */ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in ++ DT_MIPS_DELTA_SYM. */ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the ++ class declaration. */ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in ++ DT_MIPS_DELTA_CLASSSYM. */ ++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ ++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve ++ function stored in GOT. */ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added ++ by rld on dlopen() calls. */ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ ++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ ++/* The address of .got.plt in an executable using the new non-PIC ABI. */ ++#define DT_MIPS_PLTGOT 0x70000032 ++/* The base of the PLT in an executable using the new non-PIC ABI if that ++ PLT is writable. For a non-writable PLT, this is omitted or has a zero ++ value. */ ++#define DT_MIPS_RWPLT 0x70000034 ++#define DT_MIPS_NUM 0x35 ++ ++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ ++ ++#define RHF_NONE 0 /* No flags */ ++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ ++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++/* Entries found in sections of type SHT_MIPS_LIBLIST. */ ++ ++typedef struct ++{ ++ Elf32_Word l_name; /* Name (string table index) */ ++ Elf32_Word l_time_stamp; /* Timestamp */ ++ Elf32_Word l_checksum; /* Checksum */ ++ Elf32_Word l_version; /* Interface version */ ++ Elf32_Word l_flags; /* Flags */ ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; /* Name (string table index) */ ++ Elf64_Word l_time_stamp; /* Timestamp */ ++ Elf64_Word l_checksum; /* Checksum */ ++ Elf64_Word l_version; /* Interface version */ ++ Elf64_Word l_flags; /* Flags */ ++} Elf64_Lib; ++ ++ ++/* Legal values for l_flags. */ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ ++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++/* Entries found in sections of type SHT_MIPS_CONFLICT. */ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++/* HPPA specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ ++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ ++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ ++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ ++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch ++ prediction. */ ++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ ++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ ++ ++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ ++ ++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ ++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ ++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ ++ ++/* Additional section indeces. */ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared ++ symbols in ANSI C. */ ++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ ++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ ++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ ++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ ++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++/* HPPA relocs. */ ++ ++#define R_PARISC_NONE 0 /* No reloc. */ ++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ ++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ ++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ ++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ ++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ ++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ ++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ ++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ ++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ ++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ ++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ ++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ ++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ ++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ ++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ ++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ ++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ ++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ ++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ ++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ ++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ ++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ ++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ ++#define R_PARISC_FPTR64 64 /* 64 bits function address. */ ++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ ++#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ ++#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ ++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ ++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ ++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ ++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ ++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ ++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ ++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ ++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ ++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ ++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ ++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ ++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ ++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ ++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ ++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 /* Copy relocation. */ ++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ ++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ ++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ ++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ ++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ ++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ ++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ ++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ ++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_GNU_VTENTRY 232 ++#define R_PARISC_GNU_VTINHERIT 233 ++#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ ++#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ ++#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ ++#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ ++#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ ++#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ ++#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ ++#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ ++#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ ++#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ ++#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ ++#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ ++#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L ++#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R ++#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L ++#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R ++#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 ++#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 ++#define R_PARISC_HIRESERVE 255 ++ ++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++/* Alpha specific definitions. */ ++ ++/* Legal values for e_flags field of Elf64_Ehdr. */ ++ ++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ ++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ ++ ++/* Legal values for sh_type field of Elf64_Shdr. */ ++ ++/* These two are primerily concerned with ECOFF debugging info. */ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++/* Legal values for sh_flags field of Elf64_Shdr. */ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++/* Legal values for st_other field of Elf64_Sym. */ ++#define STO_ALPHA_NOPV 0x80 /* No PV required. */ ++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ ++ ++/* Alpha relocs. */ ++ ++#define R_ALPHA_NONE 0 /* No reloc */ ++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ ++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ ++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ ++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ ++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ ++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ ++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ ++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ ++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ ++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ ++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ ++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ ++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ ++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ ++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ ++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ ++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ ++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++/* Keep this the last entry. */ ++#define R_ALPHA_NUM 46 ++ ++/* Magic values of the LITUSE relocation addend. */ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++/* Legal values for d_tag of Elf64_Dyn. */ ++#define DT_ALPHA_PLTRO (DT_LOPROC + 0) ++#define DT_ALPHA_NUM 1 ++ ++/* PowerPC specific declarations */ ++ ++/* Values for Elf32/64_Ehdr.e_flags. */ ++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ ++ ++/* Cygnus local bits below */ ++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib ++ flag */ ++ ++/* PowerPC relocations defined by the ABIs */ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 /* 32bit absolute address */ ++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ ++#define R_PPC_ADDR16 3 /* 16bit absolute address */ ++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ ++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ ++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ ++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 /* PC relative 26 bit */ ++#define R_PPC_REL14 11 /* PC relative 16 bit */ ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++/* PowerPC relocations defined for the TLS access ABI. */ ++#define R_PPC_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ ++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ ++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ ++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ ++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ ++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ ++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ ++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ ++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ ++ ++/* The remaining relocs are from the Embedded ELF ABI, and are not ++ in the SVR4 ELF ABI. */ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ ++ ++/* Diab tool relocations. */ ++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ ++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ ++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ ++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ ++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ ++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ ++ ++/* GNU extension to support local ifunc. */ ++#define R_PPC_IRELATIVE 248 ++ ++/* GNU relocs used in PIC code sequences. */ ++#define R_PPC_REL16 249 /* half16 (sym+add-.) */ ++#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ ++#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ ++#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ ++ ++/* This is a phony reloc to handle any old fashioned TOC16 references ++ that may still be in object files. */ ++#define R_PPC_TOC16 255 ++ ++/* PowerPC specific values for the Dyn d_tag field. */ ++#define DT_PPC_GOT (DT_LOPROC + 0) ++#define DT_PPC_NUM 1 ++ ++/* PowerPC64 relocations defined by the ABIs */ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ ++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ ++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ ++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ ++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ ++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ ++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ ++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ ++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ ++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ ++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ ++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ ++#define R_PPC64_PLT64 45 /* doubleword64 L + A */ ++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ ++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ ++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ ++#define R_PPC64_TOC 51 /* doubleword64 .TOC */ ++#define R_PPC64_PLTGOT16 52 /* half16* M + A */ ++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ ++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ ++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ ++ ++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ ++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ ++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ ++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ ++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ ++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ ++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ ++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ ++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ ++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ ++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ ++ ++/* PowerPC64 relocations defined for the TLS access ABI. */ ++#define R_PPC64_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ ++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ ++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ ++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ ++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ ++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ ++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ ++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ ++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ ++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ ++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ ++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ ++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ ++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ ++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ ++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ ++ ++/* GNU extension to support local ifunc. */ ++#define R_PPC64_JMP_IREL 247 ++#define R_PPC64_IRELATIVE 248 ++#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ ++#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ ++#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ ++#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ ++ ++/* PowerPC64 specific values for the Dyn d_tag field. */ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_OPD (DT_LOPROC + 1) ++#define DT_PPC64_OPDSZ (DT_LOPROC + 2) ++#define DT_PPC64_NUM 3 ++ ++ ++/* ARM specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++#define EF_ARM_SOFT_FLOAT 0x200 ++#define EF_ARM_VFP_FLOAT 0x400 ++#define EF_ARM_MAVERICK_FLOAT 0x800 ++ ++ ++/* Other constants defined in the ARM ELF spec. version B-01. */ ++/* NB. These conflict with values defined above. */ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++/* Constants defined in AAELF. */ ++#define EF_ARM_BE8 0x00800000 ++#define EF_ARM_LE8 0x00400000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++#define EF_ARM_EABI_VER3 0x03000000 ++#define EF_ARM_EABI_VER4 0x04000000 ++#define EF_ARM_EABI_VER5 0x05000000 ++ ++/* Additional symbol types for Thumb. */ ++#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ ++#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ ++ ++/* ARM-specific values for sh_flags */ ++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ ++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined ++ in the input to a link step. */ ++ ++/* ARM-specific program header flags */ ++#define PF_ARM_SB 0x10000000 /* Segment contains the location ++ addressed by the static base. */ ++#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ ++#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ ++#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ ++#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ ++ ++ ++/* ARM relocs. */ ++ ++#define R_ARM_NONE 0 /* No reloc */ ++#define R_ARM_PC24 1 /* PC relative 26 bit branch */ ++#define R_ARM_ABS32 2 /* Direct 32 bit */ ++#define R_ARM_REL32 3 /* PC relative 32 bit */ ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 /* Direct 16 bit */ ++#define R_ARM_ABS12 6 /* Direct 12 bit */ ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 /* Direct 8 bit */ ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_SWI24 13 /* Obsolete static relocation. */ ++#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ ++#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ ++#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ ++#define R_ARM_COPY 20 /* Copy symbol at runtime */ ++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ ++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ ++#define R_ARM_RELATIVE 23 /* Adjust by program base */ ++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ ++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ ++#define R_ARM_GOT32 26 /* 32 bit GOT entry */ ++#define R_ARM_PLT32 27 /* 32 bit PLT address */ ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_TLS_GOTDESC 90 ++#define R_ARM_TLS_CALL 91 ++#define R_ARM_TLS_DESCSEQ 92 ++#define R_ARM_THM_TLS_CALL 93 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ ++#define R_ARM_THM_PC9 103 /* thumb conditional branch */ ++#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic ++ thread local data */ ++#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic ++ thread local data */ ++#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS ++ block */ ++#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of ++ static TLS block offset */ ++#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static ++ TLS block */ ++#define R_ARM_THM_TLS_DESCSEQ 129 ++#define R_ARM_IRELATIVE 160 ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++/* Keep this the last entry. */ ++#define R_ARM_NUM 256 ++ ++/* IA-64 specific declarations. */ ++ ++/* Processor specific flags for the Ehdr e_flags field. */ ++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ ++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ ++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ ++#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) ++#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) ++#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) ++ ++/* Processor specific flags for the Phdr p_flags field. */ ++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ ++ ++/* Processor specific flags for the Shdr sh_flags field. */ ++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ ++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Dyn d_tag field. */ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++/* IA-64 relocations. */ ++#define R_IA64_NONE 0x00 /* none */ ++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ ++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ ++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ ++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ ++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ ++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ ++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ ++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ ++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ ++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ ++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ ++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ ++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ ++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ ++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ ++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ ++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ ++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ ++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ ++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ ++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ ++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ ++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ ++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ ++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ ++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ ++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ ++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ ++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ ++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ ++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ ++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ ++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ ++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ ++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ ++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ ++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ ++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ ++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ ++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ ++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ ++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ ++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ ++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ ++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ ++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ ++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ ++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ ++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ ++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ ++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ ++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ ++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ ++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ ++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ ++#define R_IA64_COPY 0x84 /* copy relocation */ ++#define R_IA64_SUB 0x85 /* Addend and symbol difference */ ++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ ++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ ++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ ++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ ++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ ++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ ++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ ++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ ++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ ++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ ++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ ++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ ++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ ++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ ++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ ++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ ++ ++/* SH specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_SH_MACH_MASK 0x1f ++#define EF_SH_UNKNOWN 0x0 ++#define EF_SH1 0x1 ++#define EF_SH2 0x2 ++#define EF_SH3 0x3 ++#define EF_SH_DSP 0x4 ++#define EF_SH3_DSP 0x5 ++#define EF_SH4AL_DSP 0x6 ++#define EF_SH3E 0x8 ++#define EF_SH4 0x9 ++#define EF_SH2E 0xb ++#define EF_SH4A 0xc ++#define EF_SH2A 0xd ++#define EF_SH4_NOFPU 0x10 ++#define EF_SH4A_NOFPU 0x11 ++#define EF_SH4_NOMMU_NOFPU 0x12 ++#define EF_SH2A_NOFPU 0x13 ++#define EF_SH3_NOMMU 0x14 ++#define EF_SH2A_SH4_NOFPU 0x15 ++#define EF_SH2A_SH3_NOFPU 0x16 ++#define EF_SH2A_SH4 0x17 ++#define EF_SH2A_SH3E 0x18 ++ ++/* SH relocs. */ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++/* Keep this the last entry. */ ++#define R_SH_NUM 256 ++ ++/* S/390 specific definitions. */ ++ ++/* Valid values for the e_flags field. */ ++ ++#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ ++ ++/* Additional s390 relocs */ ++ ++#define R_390_NONE 0 /* No reloc. */ ++#define R_390_8 1 /* Direct 8 bit. */ ++#define R_390_12 2 /* Direct 12 bit. */ ++#define R_390_16 3 /* Direct 16 bit. */ ++#define R_390_32 4 /* Direct 32 bit. */ ++#define R_390_PC32 5 /* PC relative 32 bit. */ ++#define R_390_GOT12 6 /* 12 bit GOT offset. */ ++#define R_390_GOT32 7 /* 32 bit GOT offset. */ ++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ ++#define R_390_COPY 9 /* Copy symbol at runtime. */ ++#define R_390_GLOB_DAT 10 /* Create GOT entry. */ ++#define R_390_JMP_SLOT 11 /* Create PLT entry. */ ++#define R_390_RELATIVE 12 /* Adjust by program base. */ ++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ ++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ ++#define R_390_GOT16 15 /* 16 bit GOT offset. */ ++#define R_390_PC16 16 /* PC relative 16 bit. */ ++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ ++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ ++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ ++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ ++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ ++#define R_390_64 22 /* Direct 64 bit. */ ++#define R_390_PC64 23 /* PC relative 64 bit. */ ++#define R_390_GOT64 24 /* 64 bit GOT offset. */ ++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ ++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ ++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ ++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ ++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ ++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ ++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ ++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ ++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ ++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ ++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ ++#define R_390_TLS_GDCALL 38 /* Tag for function call in general ++ dynamic TLS code. */ ++#define R_390_TLS_LDCALL 39 /* Tag for function call in local ++ dynamic TLS code. */ ++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ ++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ ++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS ++ block. */ ++#define R_390_20 57 /* Direct 20 bit. */ ++#define R_390_GOT20 58 /* 20 bit GOT offset. */ ++#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ ++#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ ++/* Keep this the last entry. */ ++#define R_390_NUM 62 ++ ++ ++/* CRIS relocations. */ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++ ++/* AMD x86-64 relocations. */ ++#define R_X86_64_NONE 0 /* No reloc */ ++#define R_X86_64_64 1 /* Direct 64 bit */ ++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ ++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ ++#define R_X86_64_PLT32 4 /* 32 bit PLT address */ ++#define R_X86_64_COPY 5 /* Copy symbol at runtime */ ++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ ++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ ++#define R_X86_64_RELATIVE 8 /* Adjust by program base */ ++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative ++ offset to GOT */ ++#define R_X86_64_32 10 /* Direct 32 bit zero extended */ ++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ ++#define R_X86_64_16 12 /* Direct 16 bit zero extended */ ++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ ++#define R_X86_64_8 14 /* Direct 8 bit sign extended */ ++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ ++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ ++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ ++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ ++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset ++ to two GOT entries for GD symbol */ ++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset ++ to two GOT entries for LD symbol */ ++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ ++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset ++ to GOT entry for IE symbol */ ++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ ++#define R_X86_64_PC64 24 /* PC relative 64 bit */ ++#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ ++#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative ++ offset to GOT */ ++#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ ++#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset ++ to GOT entry */ ++#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ ++#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ ++#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset ++ to PLT entry */ ++#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ ++#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ ++#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ ++#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS ++ descriptor. */ ++#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ ++#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ ++#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ ++ ++#define R_X86_64_NUM 39 ++ ++ ++/* AM33 relocations. */ ++#define R_MN10300_NONE 0 /* No reloc. */ ++#define R_MN10300_32 1 /* Direct 32 bit. */ ++#define R_MN10300_16 2 /* Direct 16 bit. */ ++#define R_MN10300_8 3 /* Direct 8 bit. */ ++#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ ++#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ ++#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ ++#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ ++#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ ++#define R_MN10300_24 9 /* Direct 24 bit. */ ++#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ ++#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ ++#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ ++#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ ++#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ ++#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ ++#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ ++#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ ++#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ ++#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ ++#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ ++#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ ++#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ ++#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ ++ ++#define R_MN10300_NUM 24 ++ ++ ++/* M32R relocs. */ ++#define R_M32R_NONE 0 /* No reloc. */ ++#define R_M32R_16 1 /* Direct 16 bit. */ ++#define R_M32R_32 2 /* Direct 32 bit. */ ++#define R_M32R_24 3 /* Direct 24 bit. */ ++#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ ++#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ ++#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ ++#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ ++#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ ++#define R_M32R_LO16 9 /* Low 16 bit. */ ++#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ ++#define R_M32R_GNU_VTINHERIT 11 ++#define R_M32R_GNU_VTENTRY 12 ++/* M32R relocs use SHT_RELA. */ ++#define R_M32R_16_RELA 33 /* Direct 16 bit. */ ++#define R_M32R_32_RELA 34 /* Direct 32 bit. */ ++#define R_M32R_24_RELA 35 /* Direct 24 bit. */ ++#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ ++#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ ++#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ ++#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ ++#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ ++#define R_M32R_LO16_RELA 41 /* Low 16 bit */ ++#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ ++#define R_M32R_RELA_GNU_VTINHERIT 43 ++#define R_M32R_RELA_GNU_VTENTRY 44 ++#define R_M32R_REL32 45 /* PC relative 32 bit. */ ++ ++#define R_M32R_GOT24 48 /* 24 bit GOT entry */ ++#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ ++#define R_M32R_COPY 50 /* Copy symbol at runtime */ ++#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ ++#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ ++#define R_M32R_RELATIVE 53 /* Adjust by program base */ ++#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ ++#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ ++#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned ++ low */ ++#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed ++ low */ ++#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ ++#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to ++ GOT with unsigned low */ ++#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to ++ GOT with signed low */ ++#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to ++ GOT */ ++#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT ++ with unsigned low */ ++#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT ++ with signed low */ ++#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ ++#define R_M32R_NUM 256 /* Keep this the last entry. */ ++ ++ ++/* TILEPro relocations. */ ++#define R_TILEPRO_NONE 0 /* No reloc */ ++#define R_TILEPRO_32 1 /* Direct 32 bit */ ++#define R_TILEPRO_16 2 /* Direct 16 bit */ ++#define R_TILEPRO_8 3 /* Direct 8 bit */ ++#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ ++#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ ++#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ ++#define R_TILEPRO_LO16 7 /* Low 16 bit */ ++#define R_TILEPRO_HI16 8 /* High 16 bit */ ++#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ ++#define R_TILEPRO_COPY 10 /* Copy relocation */ ++#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ ++#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ ++#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ ++#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ ++#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ ++#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ ++#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ ++#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ ++#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ ++#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ ++#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ ++#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ ++#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ ++#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ ++#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ ++#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ ++#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ ++#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ ++#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ ++#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ ++#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ ++#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ ++#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ ++#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ ++#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ ++#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ ++#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ ++#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ ++#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ ++#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ ++#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ ++#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ ++#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ ++#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ ++#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ ++#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ ++#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ ++#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ ++/* Relocs 56-59 are currently not defined. */ ++#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ ++#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ ++#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ ++#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ ++#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ ++#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ ++#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ ++#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ ++#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ ++#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ ++#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ ++ ++#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ ++#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ ++ ++#define R_TILEPRO_NUM 130 ++ ++ ++/* TILE-Gx relocations. */ ++#define R_TILEGX_NONE 0 /* No reloc */ ++#define R_TILEGX_64 1 /* Direct 64 bit */ ++#define R_TILEGX_32 2 /* Direct 32 bit */ ++#define R_TILEGX_16 3 /* Direct 16 bit */ ++#define R_TILEGX_8 4 /* Direct 8 bit */ ++#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ ++#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ ++#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ ++#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ ++#define R_TILEGX_HW0 9 /* hword 0 16-bit */ ++#define R_TILEGX_HW1 10 /* hword 1 16-bit */ ++#define R_TILEGX_HW2 11 /* hword 2 16-bit */ ++#define R_TILEGX_HW3 12 /* hword 3 16-bit */ ++#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ ++#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ ++#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ ++#define R_TILEGX_COPY 16 /* Copy relocation */ ++#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ ++#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ ++#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ ++#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ ++#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ ++#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ ++#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ ++#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ ++#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ ++#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ ++#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ ++#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ ++#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ ++#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ ++#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ ++#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ ++#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ ++#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ ++#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ ++#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ ++#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ ++#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ ++#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ ++#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ ++#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ ++#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ ++#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ ++#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ ++/* Relocs 66-71 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ ++/* Relocs 76-77 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ ++#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ ++#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ ++#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ ++/* Relocs 90-91 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ ++#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ ++/* Relocs 94-99 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ ++/* Relocs 104-105 are currently not defined. */ ++#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ ++#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ ++#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ ++#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ ++#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ ++#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ ++#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ ++#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ ++#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ ++#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ ++#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ ++#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ ++#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ ++#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ ++#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ ++#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ ++ ++#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ ++#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ ++ ++#define R_TILEGX_NUM 130 ++ ++#endif /* elf.h */ +--- a/scripts/mod/mk_elfconfig.c ++++ b/scripts/mod/mk_elfconfig.c +@@ -2,7 +2,11 @@ + #include + #include + #include ++#ifndef __APPLE__ + #include ++#else ++#include "elf.h" ++#endif + + int + main(int argc, char **argv) +--- a/scripts/mod/modpost.h ++++ b/scripts/mod/modpost.h +@@ -10,7 +10,11 @@ + #include + #include + #include ++#if !(defined(__APPLE__) || defined(__CYGWIN__)) + #include ++#else ++#include "elf.h" ++#endif + #include "../../include/linux/module_symbol.h" + + #include diff --git a/target/linux/generic/hack-6.12/211-darwin-uuid-typedef-clash.patch b/target/linux/generic/hack-6.12/211-darwin-uuid-typedef-clash.patch new file mode 100644 index 00000000000..c0e0b24e3ca --- /dev/null +++ b/target/linux/generic/hack-6.12/211-darwin-uuid-typedef-clash.patch @@ -0,0 +1,22 @@ +From e44fc2af1ddc452b6659d08c16973d65c73b7d0a Mon Sep 17 00:00:00 2001 +From: Kevin Darbyshire-Bryant +Date: Wed, 5 Feb 2020 18:36:43 +0000 +Subject: [PATCH] file2alias: build on macos + +Signed-off-by: Kevin Darbyshire-Bryant +--- + scripts/mod/file2alias.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/scripts/mod/file2alias.c ++++ b/scripts/mod/file2alias.c +@@ -35,6 +35,9 @@ typedef uint32_t __u32; + typedef uint16_t __u16; + typedef unsigned char __u8; + ++#ifdef __APPLE__ ++#define uuid_t compat_uuid_t ++#endif + /* UUID types for backward compatibility, don't use in new code */ + typedef struct { + __u8 b[16]; diff --git a/target/linux/generic/hack-6.12/214-spidev_h_portability.patch b/target/linux/generic/hack-6.12/214-spidev_h_portability.patch new file mode 100644 index 00000000000..db754a29033 --- /dev/null +++ b/target/linux/generic/hack-6.12/214-spidev_h_portability.patch @@ -0,0 +1,24 @@ +From be9be95ff10e16a5b4ad36f903978d0cc5747024 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:04:08 +0200 +Subject: kernel: fix linux/spi/spidev.h portability issues with musl + +Felix will try to get this define included into musl + +lede-commit: 795e7cf60de19e7a076a46874fab7bb88b43bbff +Signed-off-by: Felix Fietkau +--- + include/uapi/linux/spi/spidev.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/uapi/linux/spi/spidev.h ++++ b/include/uapi/linux/spi/spidev.h +@@ -93,7 +93,7 @@ struct spi_ioc_transfer { + + /* not all platforms use or _IOC_TYPECHECK() ... */ + #define SPI_MSGSIZE(N) \ +- ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \ ++ ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << 13)) \ + ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) + #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) + diff --git a/target/linux/generic/hack-6.12/230-openwrt_lzma_options.patch b/target/linux/generic/hack-6.12/230-openwrt_lzma_options.patch new file mode 100644 index 00000000000..20f2b29bf19 --- /dev/null +++ b/target/linux/generic/hack-6.12/230-openwrt_lzma_options.patch @@ -0,0 +1,38 @@ +From b3d00b452467f621317953d9e4c6f9ae8dcfd271 Mon Sep 17 00:00:00 2001 +From: Imre Kaloz +Date: Fri, 7 Jul 2017 17:06:55 +0200 +Subject: use the openwrt lzma options for now + +lede-commit: 548de949f392049420a6a1feeef118b30ab8ea8c +Signed-off-by: Imre Kaloz +--- + lib/decompress.c | 1 + + scripts/Makefile.lib | 2 +- + usr/gen_initramfs_list.sh | 10 +++++----- + 3 files changed, 7 insertions(+), 6 deletions(-) + +--- a/lib/decompress.c ++++ b/lib/decompress.c +@@ -53,6 +53,7 @@ static const struct compress_format comp + { {0x1f, 0x9e}, "gzip", gunzip }, + { {0x42, 0x5a}, "bzip2", bunzip2 }, + { {0x5d, 0x00}, "lzma", unlzma }, ++ { {0x6d, 0x00}, "lzma-openwrt", unlzma }, + { {0xfd, 0x37}, "xz", unxz }, + { {0x89, 0x4c}, "lzo", unlzo }, + { {0x02, 0x21}, "lz4", unlz4 }, +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -359,10 +359,10 @@ quiet_cmd_bzip2_with_size = BZIP2 $@ + # --------------------------------------------------------------------------- + + quiet_cmd_lzma = LZMA $@ +- cmd_lzma = cat $(real-prereqs) | $(LZMA) -9 > $@ ++ cmd_lzma = cat $(real-prereqs) | $(LZMA) e -d20 -lc1 -lp2 -pb2 -eos -si -so > $@ + + quiet_cmd_lzma_with_size = LZMA $@ +- cmd_lzma_with_size = { cat $(real-prereqs) | $(LZMA) -9; $(size_append); } > $@ ++ cmd_lzma_with_size = { cat $(real-prereqs) | $(LZMA) e -d20 -lc1 -lp2 -pb2 -eos -si -so; $(size_append); } > $@ + + quiet_cmd_lzo = LZO $@ + cmd_lzo = cat $(real-prereqs) | $(KLZOP) -9 > $@ diff --git a/target/linux/generic/hack-6.12/249-udp-tunnel-selection.patch b/target/linux/generic/hack-6.12/249-udp-tunnel-selection.patch new file mode 100644 index 00000000000..7f7e5f11f4f --- /dev/null +++ b/target/linux/generic/hack-6.12/249-udp-tunnel-selection.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andy Chiang +Date: Thu, 20 Nov 2025 20:37:53 +0700 +Subject: [PATCH] kernel: fix build of kmod-udptunnel4 and kmod-udptunnel6 + +This fixes the following errors: +``` +make[4]: Entering directory '/home/runner/work/OP/OP/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_filogic/linux-6.12.57' + MODPOST /home/runner/work/OP/OP/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_filogic/ovpn-dco-0.2.20250801/drivers/net/ovpn-dco/Module.symvers +ERROR: modpost: "udp_tunnel6_xmit_skb" [/home/runner/work/OP/OP/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_filogic/ovpn-dco-0.2.20250801/drivers/net/ovpn-dco/ovpn-dco-v2.ko] undefined! +ERROR: modpost: "setup_udp_tunnel_sock" [/home/runner/work/OP/OP/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_filogic/ovpn-dco-0.2.20250801/drivers/net/ovpn-dco/ovpn-dco-v2.ko] undefined! +ERROR: modpost: "udp_tunnel_xmit_skb" [/home/runner/work/OP/OP/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_filogic/ovpn-dco-0.2.20250801/drivers/net/ovpn-dco/ovpn-dco-v2.ko] undefined! +make[6]: *** [scripts/Makefile.modpost:145: /home/runner/work/OP/OP/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_filogic/ovpn-dco-0.2.20250801/drivers/net/ovpn-dco/Module.symvers] Error 1 +make[5]: *** [/home/runner/work/OP/OP/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_filogic/linux-6.12.57/Makefile:1897: modpost] Error 2 +make[4]: *** [Makefile:224: __sub-make] Error 2 +``` + +ref https://github.com/openwrt/openwrt/commit/1d15a96b29dcd0947690951a7c36aead79a27129 +fixes: https://github.com/openwrt/packages/issues/22998 + +Signed-off-by: Andy Chiang +--- + net/ipv4/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/ipv4/Kconfig ++++ b/net/ipv4/Kconfig +@@ -315,7 +315,7 @@ config NET_IPVTI + on top. + + config NET_UDP_TUNNEL +- tristate ++ tristate "IP: UDP tunneling support" + select NET_IP_TUNNEL + default n + diff --git a/target/linux/generic/hack-6.12/250-netfilter_depends.patch b/target/linux/generic/hack-6.12/250-netfilter_depends.patch new file mode 100644 index 00000000000..43faa9959ed --- /dev/null +++ b/target/linux/generic/hack-6.12/250-netfilter_depends.patch @@ -0,0 +1,27 @@ +From: Felix Fietkau +Subject: hack: net: remove bogus netfilter dependencies + +lede-commit: 589d2a377dee27d206fc3725325309cf649e4df6 +Signed-off-by: Felix Fietkau +--- + net/netfilter/Kconfig | 2 -- + 1 file changed, 2 deletions(-) + +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -259,7 +259,6 @@ config NF_CONNTRACK_FTP + + config NF_CONNTRACK_H323 + tristate "H.323 protocol support" +- depends on IPV6 || IPV6=n + depends on NETFILTER_ADVANCED + help + H.323 is a VoIP signalling protocol from ITU-T. As one of the most +@@ -1120,7 +1119,6 @@ config NETFILTER_XT_TARGET_SECMARK + + config NETFILTER_XT_TARGET_TCPMSS + tristate '"TCPMSS" target support' +- depends on IPV6 || IPV6=n + default m if NETFILTER_ADVANCED=n + help + This option adds a `TCPMSS' target, which allows you to alter the diff --git a/target/linux/generic/hack-6.12/251-kconfig.patch b/target/linux/generic/hack-6.12/251-kconfig.patch new file mode 100644 index 00000000000..dd73e794535 --- /dev/null +++ b/target/linux/generic/hack-6.12/251-kconfig.patch @@ -0,0 +1,157 @@ +From da3c50704f14132f4adf80d48e9a4cd5d46e54c9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 7 Jul 2017 17:09:21 +0200 +Subject: kconfig: owrt specifc dependencies + +Signed-off-by: John Crispin +--- + crypto/Kconfig | 10 +++++----- + drivers/bcma/Kconfig | 1 + + drivers/ssb/Kconfig | 3 ++- + lib/Kconfig | 8 ++++---- + net/netfilter/Kconfig | 2 +- + net/wireless/Kconfig | 17 ++++++++++------- + sound/core/Kconfig | 4 ++-- + 7 files changed, 25 insertions(+), 20 deletions(-) + +--- a/crypto/Kconfig ++++ b/crypto/Kconfig +@@ -55,7 +55,7 @@ config CRYPTO_FIPS_VERSION + By default the KERNELRELEASE value is used. + + config CRYPTO_ALGAPI +- tristate ++ tristate "ALGAPI" + select CRYPTO_ALGAPI2 + help + This option provides the API for cryptographic algorithms. +@@ -64,7 +64,7 @@ config CRYPTO_ALGAPI2 + tristate + + config CRYPTO_AEAD +- tristate ++ tristate "AEAD" + select CRYPTO_AEAD2 + select CRYPTO_ALGAPI + +@@ -82,7 +82,7 @@ config CRYPTO_SIG2 + select CRYPTO_ALGAPI2 + + config CRYPTO_SKCIPHER +- tristate ++ tristate "SKCIPHER" + select CRYPTO_SKCIPHER2 + select CRYPTO_ALGAPI + select CRYPTO_ECB +@@ -92,7 +92,7 @@ config CRYPTO_SKCIPHER2 + select CRYPTO_ALGAPI2 + + config CRYPTO_HASH +- tristate ++ tristate "HASH" + select CRYPTO_HASH2 + select CRYPTO_ALGAPI + +@@ -101,7 +101,7 @@ config CRYPTO_HASH2 + select CRYPTO_ALGAPI2 + + config CRYPTO_RNG +- tristate ++ tristate "RNG" + select CRYPTO_RNG2 + select CRYPTO_ALGAPI + +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -16,6 +16,7 @@ if BCMA + # Support for Block-I/O. SELECT this from the driver that needs it. + config BCMA_BLOCKIO + bool ++ default y + + config BCMA_HOST_PCI_POSSIBLE + bool +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -29,6 +29,7 @@ config SSB_SPROM + config SSB_BLOCKIO + bool + depends on SSB ++ default y + + config SSB_PCIHOST_POSSIBLE + bool +@@ -49,7 +50,7 @@ config SSB_PCIHOST + config SSB_B43_PCI_BRIDGE + bool + depends on SSB_PCIHOST +- default n ++ default y + + config SSB_PCMCIAHOST_POSSIBLE + bool +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -457,16 +457,16 @@ config BCH_CONST_T + # Textsearch support is select'ed if needed + # + config TEXTSEARCH +- bool ++ bool "Textsearch support" + + config TEXTSEARCH_KMP +- tristate ++ tristate "Textsearch KMP" + + config TEXTSEARCH_BM +- tristate ++ tristate "Textsearch BM" + + config TEXTSEARCH_FSM +- tristate ++ tristate "Textsearch FSM" + + config BTREE + bool +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -22,7 +22,7 @@ config NETFILTER_SKIP_EGRESS + def_bool NETFILTER_EGRESS && (NET_CLS_ACT || IFB) + + config NETFILTER_NETLINK +- tristate ++ tristate "Netfilter NFNETLINK interface" + + config NETFILTER_FAMILY_BRIDGE + bool +--- a/sound/core/Kconfig ++++ b/sound/core/Kconfig +@@ -17,7 +17,7 @@ config SND_DMAENGINE_PCM + tristate + + config SND_HWDEP +- tristate ++ tristate "Sound hardware support" + + config SND_SEQ_DEVICE + tristate +@@ -57,7 +57,7 @@ config SND_CORE_TEST + + + config SND_COMPRESS_OFFLOAD +- tristate ++ tristate "Compression offloading support" + + config SND_JACK + bool +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -484,7 +484,7 @@ config NET_DEVLINK + default n + + config PAGE_POOL +- bool ++ bool "Page pool support" + + config PAGE_POOL_STATS + default n diff --git a/target/linux/generic/hack-6.12/253-ksmbd-config.patch b/target/linux/generic/hack-6.12/253-ksmbd-config.patch new file mode 100644 index 00000000000..d9587fbe843 --- /dev/null +++ b/target/linux/generic/hack-6.12/253-ksmbd-config.patch @@ -0,0 +1,32 @@ +From dcd966fa7ca63f38cf7147e1184d13d66e2ca340 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:33:30 +0200 +Subject: [PATCH] Kconfig: add tristate for OID and ASNI string + +--- + init/Kconfig | 2 +- + lib/Kconfig | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -2063,7 +2063,7 @@ config PADATA + bool + + config ASN1 +- tristate ++ tristate "ASN1" + help + Build a simple ASN.1 grammar compiler that produces a bytecode output + that can be interpreted by the ASN.1 stream decoder and used to +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -642,7 +642,7 @@ config LIBFDT + bool + + config OID_REGISTRY +- tristate ++ tristate "OID" + help + Enable fast lookup object identifier registry. + diff --git a/target/linux/generic/hack-6.12/259-regmap_dynamic.patch b/target/linux/generic/hack-6.12/259-regmap_dynamic.patch new file mode 100644 index 00000000000..4f48fc06905 --- /dev/null +++ b/target/linux/generic/hack-6.12/259-regmap_dynamic.patch @@ -0,0 +1,156 @@ +From 811d9e2268a62b830cfe93cd8bc929afcb8b198b Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 15 Jul 2017 21:12:38 +0200 +Subject: kernel: move regmap bloat out of the kernel image if it is only being used in modules + +lede-commit: 96f39119815028073583e4fca3a9c5fe9141e998 +Signed-off-by: Felix Fietkau +--- + drivers/base/regmap/Kconfig | 15 ++++++++++----- + drivers/base/regmap/Makefile | 12 ++++++++---- + drivers/base/regmap/regmap.c | 3 +++ + include/linux/regmap.h | 2 +- + 4 files changed, 22 insertions(+), 10 deletions(-) + +--- a/drivers/base/regmap/Kconfig ++++ b/drivers/base/regmap/Kconfig +@@ -4,8 +4,7 @@ + # subsystems should select the appropriate symbols. + + config REGMAP +- bool +- default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO || REGMAP_FSI) ++ tristate + select IRQ_DOMAIN if REGMAP_IRQ + select MDIO_BUS if REGMAP_MDIO + help +@@ -19,7 +18,7 @@ config REGMAP + + config REGMAP_KUNIT + tristate "KUnit tests for regmap" +- depends on KUNIT && REGMAP ++ depends on KUNIT + default KUNIT_ALL_TESTS + select REGMAP_RAM + +@@ -34,60 +33,76 @@ config REGMAP_BUILD + normally enabled. + + config REGMAP_AC97 ++ select REGMAP + tristate + + config REGMAP_I2C ++ select REGMAP + tristate + depends on I2C + + config REGMAP_SLIMBUS ++ select REGMAP + tristate + depends on SLIMBUS + + config REGMAP_SPI ++ select REGMAP + tristate + depends on SPI + + config REGMAP_SPMI ++ select REGMAP + tristate + depends on SPMI + + config REGMAP_W1 ++ select REGMAP + tristate + depends on W1 + + config REGMAP_MDIO ++ select REGMAP + tristate + + config REGMAP_MMIO ++ select REGMAP + tristate + + config REGMAP_IRQ ++ select REGMAP + bool + + config REGMAP_RAM ++ select REGMAP + tristate + + config REGMAP_SOUNDWIRE ++ select REGMAP + tristate + depends on SOUNDWIRE + + config REGMAP_SOUNDWIRE_MBQ ++ select REGMAP + tristate + depends on SOUNDWIRE + + config REGMAP_SCCB ++ select REGMAP + tristate + depends on I2C + + config REGMAP_I3C ++ select REGMAP + tristate + depends on I3C + + config REGMAP_SPI_AVMM ++ select REGMAP + tristate + depends on SPI + + config REGMAP_FSI ++ select REGMAP + tristate + depends on FSI +--- a/drivers/base/regmap/Makefile ++++ b/drivers/base/regmap/Makefile +@@ -2,9 +2,11 @@ + # For include/trace/define_trace.h to include trace.h + CFLAGS_regmap.o := -I$(src) + +-obj-$(CONFIG_REGMAP) += regmap.o regcache.o +-obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o regcache-maple.o +-obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o ++regmap-core-objs = regmap.o regcache.o regcache-rbtree.o regcache-flat.o regcache-maple.o ++ifdef CONFIG_DEBUG_FS ++regmap-core-objs += regmap-debugfs.o ++endif ++obj-$(CONFIG_REGMAP) += regmap-core.o + obj-$(CONFIG_REGMAP_KUNIT) += regmap-kunit.o + obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o + obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o +--- a/drivers/base/regmap/regmap.c ++++ b/drivers/base/regmap/regmap.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -3523,3 +3524,5 @@ static int __init regmap_initcall(void) + return 0; + } + postcore_initcall(regmap_initcall); ++ ++MODULE_LICENSE("GPL"); +--- a/include/linux/regmap.h ++++ b/include/linux/regmap.h +@@ -197,7 +197,7 @@ struct reg_sequence { + __ret ?: __tmp; \ + }) + +-#ifdef CONFIG_REGMAP ++#if IS_REACHABLE(CONFIG_REGMAP) + + enum regmap_endian { + /* Unspecified -> 0 -> Backwards compatible default */ diff --git a/target/linux/generic/hack-6.12/260-crypto_test_dependencies.patch b/target/linux/generic/hack-6.12/260-crypto_test_dependencies.patch new file mode 100644 index 00000000000..ce900a87ea7 --- /dev/null +++ b/target/linux/generic/hack-6.12/260-crypto_test_dependencies.patch @@ -0,0 +1,54 @@ +From fd1799b0bf5efa46dd3e6dfbbf3955564807e508 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:12:51 +0200 +Subject: kernel: prevent cryptomgr from pulling in useless extra dependencies for tests that are not run + +Reduces kernel size after LZMA by about 5k on MIPS + +lede-commit: 044c316167e076479a344c59905e5b435b84a77f +Signed-off-by: Felix Fietkau +--- + crypto/Kconfig | 13 ++++++------- + crypto/algboss.c | 4 ++++ + 2 files changed, 10 insertions(+), 7 deletions(-) + +--- a/crypto/Kconfig ++++ b/crypto/Kconfig +@@ -149,15 +149,15 @@ config CRYPTO_MANAGER + cbc(aes). + + config CRYPTO_MANAGER2 +- def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y) +- select CRYPTO_ACOMP2 +- select CRYPTO_AEAD2 +- select CRYPTO_AKCIPHER2 +- select CRYPTO_SIG2 +- select CRYPTO_HASH2 +- select CRYPTO_KPP2 +- select CRYPTO_RNG2 +- select CRYPTO_SKCIPHER2 ++ def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y && !CRYPTO_MANAGER_DISABLE_TESTS) ++ select CRYPTO_ACOMP2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_AEAD2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_AKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_SIG2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_HASH2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_KPP2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_RNG2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_SKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS + + config CRYPTO_USER + tristate "Userspace cryptographic algorithm configuration" +--- a/crypto/algboss.c ++++ b/crypto/algboss.c +@@ -203,6 +203,10 @@ static int cryptomgr_schedule_test(struc + memcpy(param->alg, alg->cra_name, sizeof(param->alg)); + param->type = alg->cra_flags; + ++#ifdef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS ++ param->type |= CRYPTO_ALG_TESTED; ++#endif ++ + thread = kthread_run(cryptomgr_test, param, "cryptomgr_test"); + if (IS_ERR(thread)) + goto err_free_param; diff --git a/target/linux/generic/hack-6.12/261-lib-arc4-unhide.patch b/target/linux/generic/hack-6.12/261-lib-arc4-unhide.patch new file mode 100644 index 00000000000..f1e1ad4c403 --- /dev/null +++ b/target/linux/generic/hack-6.12/261-lib-arc4-unhide.patch @@ -0,0 +1,24 @@ +From 241e5d3f7b0dd3c01f8c7fa83cbc9a3882286d53 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:35:18 +0200 +Subject: [PATCH] lib/crypto: add tristate string for ARC4 + +This makes it possible to select CONFIG_CRYPTO_LIB_ARC4 directly. We +need this to be able to compile this into the kernel and make use of it +from backports. + +--- + lib/crypto/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/lib/crypto/Kconfig ++++ b/lib/crypto/Kconfig +@@ -20,7 +20,7 @@ config CRYPTO_LIB_AESGCM + select CRYPTO_LIB_UTILS + + config CRYPTO_LIB_ARC4 +- tristate ++ tristate "ARC4 cipher library" + + config CRYPTO_LIB_GF128MUL + tristate diff --git a/target/linux/generic/hack-6.12/280-rfkill-stubs.patch b/target/linux/generic/hack-6.12/280-rfkill-stubs.patch new file mode 100644 index 00000000000..ff6638f7a05 --- /dev/null +++ b/target/linux/generic/hack-6.12/280-rfkill-stubs.patch @@ -0,0 +1,84 @@ +From 236c1acdfef5958010ac9814a9872e0a46fd78ee Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 7 Jul 2017 17:13:44 +0200 +Subject: rfkill: add fake rfkill support + +allow building of modules depending on RFKILL even if RFKILL is not enabled. + +Signed-off-by: John Crispin +--- + include/linux/rfkill.h | 2 +- + net/Makefile | 2 +- + net/rfkill/Kconfig | 14 +++++++++----- + net/rfkill/Makefile | 2 +- + 4 files changed, 12 insertions(+), 8 deletions(-) + +--- a/include/linux/rfkill.h ++++ b/include/linux/rfkill.h +@@ -64,7 +64,7 @@ struct rfkill_ops { + int (*set_block)(void *data, bool blocked); + }; + +-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) ++#if defined(CONFIG_RFKILL_FULL) || defined(CONFIG_RFKILL_FULL_MODULE) + /** + * rfkill_alloc - Allocate rfkill structure + * @name: name of the struct -- the string is not copied internally +--- a/net/Makefile ++++ b/net/Makefile +@@ -51,7 +51,7 @@ obj-$(CONFIG_TIPC) += tipc/ + obj-$(CONFIG_NETLABEL) += netlabel/ + obj-$(CONFIG_IUCV) += iucv/ + obj-$(CONFIG_SMC) += smc/ +-obj-$(CONFIG_RFKILL) += rfkill/ ++obj-$(CONFIG_RFKILL_FULL) += rfkill/ + obj-$(CONFIG_NET_9P) += 9p/ + obj-$(CONFIG_CAIF) += caif/ + obj-$(CONFIG_DCB) += dcb/ +--- a/net/rfkill/Kconfig ++++ b/net/rfkill/Kconfig +@@ -2,7 +2,11 @@ + # + # RF switch subsystem configuration + # +-menuconfig RFKILL ++config RFKILL ++ bool ++ default y ++ ++menuconfig RFKILL_FULL + tristate "RF switch subsystem support" + help + Say Y here if you want to have control over RF switches +@@ -14,19 +18,19 @@ menuconfig RFKILL + # LED trigger support + config RFKILL_LEDS + bool +- depends on RFKILL ++ depends on RFKILL_FULL + depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS + default y + + config RFKILL_INPUT + bool "RF switch input support" if EXPERT +- depends on RFKILL ++ depends on RFKILL_FULL + depends on INPUT = y || RFKILL = INPUT + default y if !EXPERT + + config RFKILL_GPIO + tristate "GPIO RFKILL driver" +- depends on RFKILL ++ depends on RFKILL_FULL + depends on GPIOLIB || COMPILE_TEST + default n + help +--- a/net/rfkill/Makefile ++++ b/net/rfkill/Makefile +@@ -5,5 +5,5 @@ + + rfkill-y += core.o + rfkill-$(CONFIG_RFKILL_INPUT) += input.o +-obj-$(CONFIG_RFKILL) += rfkill.o ++obj-$(CONFIG_RFKILL_FULL) += rfkill.o + obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o diff --git a/target/linux/generic/hack-6.12/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch b/target/linux/generic/hack-6.12/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch new file mode 100644 index 00000000000..6ee98ebfa25 --- /dev/null +++ b/target/linux/generic/hack-6.12/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch @@ -0,0 +1,64 @@ +From: Ben Menchaca +Date: Fri, 7 Jun 2013 18:35:22 -0500 +Subject: MIPS: r4k_cache: use more efficient cache blast + +Optimize the compiler output for larger cache blast cases that are +common for DMA-based networking. + +Signed-off-by: Ben Menchaca +Signed-off-by: Felix Fietkau +--- +--- a/arch/mips/include/asm/r4kcache.h ++++ b/arch/mips/include/asm/r4kcache.h +@@ -290,14 +290,46 @@ static inline void prot##extra##blast_## + unsigned long end) \ + { \ + unsigned long lsize = cpu_##desc##_line_size(); \ ++ unsigned long lsize_2 = lsize * 2; \ ++ unsigned long lsize_3 = lsize * 3; \ ++ unsigned long lsize_4 = lsize * 4; \ ++ unsigned long lsize_5 = lsize * 5; \ ++ unsigned long lsize_6 = lsize * 6; \ ++ unsigned long lsize_7 = lsize * 7; \ ++ unsigned long lsize_8 = lsize * 8; \ + unsigned long addr = start & ~(lsize - 1); \ +- unsigned long aend = (end - 1) & ~(lsize - 1); \ ++ unsigned long aend = (end + lsize - 1) & ~(lsize - 1); \ ++ int lines = (aend - addr) / lsize; \ + \ +- while (1) { \ ++ while (lines >= 8) { \ ++ prot##cache_op(hitop, addr); \ ++ prot##cache_op(hitop, addr + lsize); \ ++ prot##cache_op(hitop, addr + lsize_2); \ ++ prot##cache_op(hitop, addr + lsize_3); \ ++ prot##cache_op(hitop, addr + lsize_4); \ ++ prot##cache_op(hitop, addr + lsize_5); \ ++ prot##cache_op(hitop, addr + lsize_6); \ ++ prot##cache_op(hitop, addr + lsize_7); \ ++ addr += lsize_8; \ ++ lines -= 8; \ ++ } \ ++ \ ++ if (lines & 0x4) { \ ++ prot##cache_op(hitop, addr); \ ++ prot##cache_op(hitop, addr + lsize); \ ++ prot##cache_op(hitop, addr + lsize_2); \ ++ prot##cache_op(hitop, addr + lsize_3); \ ++ addr += lsize_4; \ ++ } \ ++ \ ++ if (lines & 0x2) { \ ++ prot##cache_op(hitop, addr); \ ++ prot##cache_op(hitop, addr + lsize); \ ++ addr += lsize_2; \ ++ } \ ++ \ ++ if (lines & 0x1) { \ + prot##cache_op(hitop, addr); \ +- if (addr == aend) \ +- break; \ +- addr += lsize; \ + } \ + } + diff --git a/target/linux/generic/hack-6.12/301-01-mm-permit-to-declare-custom-execmem-alloc-free-funct.patch b/target/linux/generic/hack-6.12/301-01-mm-permit-to-declare-custom-execmem-alloc-free-funct.patch new file mode 100644 index 00000000000..3128ed3a68f --- /dev/null +++ b/target/linux/generic/hack-6.12/301-01-mm-permit-to-declare-custom-execmem-alloc-free-funct.patch @@ -0,0 +1,62 @@ +From: Christian Marangi +Date: Mon, 14 Apr 2025 18:04:25 +0200 +Subject: [PATCH 1/2] mm: permit to declare custom execmem alloc/free function + +Permit to declare custom execmem alloc/free function that bypass the +execmem API. This works by making the alloc/free function weak +permitting an arch to declare a replacement for them. + +Signed-off-by: Christian Marangi +Co-authored-by: Shiji Yang +--- + include/linux/moduleloader.h | 4 ++++ + mm/execmem.c | 14 ++++++++++++-- + 2 files changed, 16 insertions(+), 2 deletions(-) + +--- a/include/linux/moduleloader.h ++++ b/include/linux/moduleloader.h +@@ -122,4 +122,8 @@ void module_arch_cleanup(struct module * + /* Any cleanup before freeing mod->module_init */ + void module_arch_freeing_init(struct module *mod); + ++enum execmem_type; ++void *arch_execmem_alloc(enum execmem_type type, size_t size); ++void arch_execmem_free(void *ptr); ++ + #endif +--- a/mm/execmem.c ++++ b/mm/execmem.c +@@ -52,14 +52,19 @@ static void *__execmem_alloc(struct exec + return kasan_reset_tag(p); + } + +-void *execmem_alloc(enum execmem_type type, size_t size) ++void *__weak arch_execmem_alloc(enum execmem_type type, size_t size) + { + struct execmem_range *range = &execmem_info->ranges[type]; + + return __execmem_alloc(range, size); + } + +-void execmem_free(void *ptr) ++void *execmem_alloc(enum execmem_type type, size_t size) ++{ ++ return arch_execmem_alloc(type, size); ++} ++ ++void __weak arch_execmem_free(void *ptr) + { + /* + * This memory may be RO, and freeing RO memory in an interrupt is not +@@ -69,6 +74,11 @@ void execmem_free(void *ptr) + vfree(ptr); + } + ++void execmem_free(void *ptr) ++{ ++ arch_execmem_free(ptr); ++} ++ + static bool execmem_validate(struct execmem_info *info) + { + struct execmem_range *r = &info->ranges[EXECMEM_DEFAULT]; diff --git a/target/linux/generic/hack-6.12/301-02-mips-replace-mlong-calls-with-mno-long-calls-if-poss.patch b/target/linux/generic/hack-6.12/301-02-mips-replace-mlong-calls-with-mno-long-calls-if-poss.patch new file mode 100644 index 00000000000..eaf4de1e896 --- /dev/null +++ b/target/linux/generic/hack-6.12/301-02-mips-replace-mlong-calls-with-mno-long-calls-if-poss.patch @@ -0,0 +1,396 @@ +From: Felix Fietkau +Date: Mon, 14 Apr 2025 18:05:45 +0200 +Subject: [PATCH 2/2] mips: replace -mlong-calls with -mno-long-calls if + possible + +This is a really old patch ported from MikroTik. It needs a an +additional patch to actually be implemented and both this and the old +one are considered HACK as they bypass normal kernel linux to make it +work. + +The original message quote: + +replace -mlong-calls with -mno-long-calls to make function +calls faster in kernel modules to achieve this, try to load +kernel modules to KSEG0 and if that doesn't work, use vmalloc +and fix up relocations with a jump table based on code from a +kernel patch by MikroTik. + +SVN-Revision: 16772 + +lede-commit: 3b3d64743ba2a874df9d70cd19e242205b0a788c +Signed-off-by: Felix Fietkau +Signed-off-by: Christian Marangi +--- + arch/mips/Makefile | 10 ++ + arch/mips/include/asm/module.h | 5 + + arch/mips/kernel/module.c | 281 ++++++++++++++++++++++++++++++++- + 3 files changed, 292 insertions(+), 4 deletions(-) + +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -97,8 +97,18 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin + cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely + cflags-y += -msoft-float -Wa,-msoft-float + LDFLAGS_vmlinux += -G 0 -static -n -nostdlib ++ifdef CONFIG_64BIT + KBUILD_AFLAGS_MODULE += -mlong-calls + KBUILD_CFLAGS_MODULE += -mlong-calls ++else ++ ifdef CONFIG_DYNAMIC_FTRACE ++ KBUILD_AFLAGS_MODULE += -mlong-calls ++ KBUILD_CFLAGS_MODULE += -mlong-calls ++ else ++ KBUILD_AFLAGS_MODULE += -mno-long-calls ++ KBUILD_CFLAGS_MODULE += -mno-long-calls ++ endif ++endif + + ifeq ($(CONFIG_RELOCATABLE),y) + LDFLAGS_vmlinux += --emit-relocs +--- a/arch/mips/include/asm/module.h ++++ b/arch/mips/include/asm/module.h +@@ -12,6 +12,11 @@ struct mod_arch_specific { + const struct exception_table_entry *dbe_start; + const struct exception_table_entry *dbe_end; + struct mips_hi16 *r_mips_hi16_list; ++ ++ void *phys_plt_tbl; ++ void *virt_plt_tbl; ++ unsigned int phys_plt_offset; ++ unsigned int virt_plt_offset; + }; + + typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */ +--- a/arch/mips/kernel/module.c ++++ b/arch/mips/kernel/module.c +@@ -8,6 +8,7 @@ + + #undef DEBUG + ++#include + #include + #include + #include +@@ -19,6 +20,7 @@ + #include + #include + #include ++#include + #include + + struct mips_hi16 { +@@ -30,14 +32,254 @@ struct mips_hi16 { + static LIST_HEAD(dbe_list); + static DEFINE_SPINLOCK(dbe_lock); + ++/* ++ * Get the potential max trampolines size required of the init and ++ * non-init sections. Only used if we cannot find enough contiguous ++ * physically mapped memory to put the module into. ++ */ ++static unsigned int ++get_plt_size(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, ++ const char *secstrings, unsigned int symindex, bool is_init) ++{ ++ unsigned long ret = 0; ++ unsigned int i, j; ++ Elf_Sym *syms; ++ ++ /* Everything marked ALLOC (this includes the exported symbols) */ ++ for (i = 1; i < hdr->e_shnum; ++i) { ++ unsigned int info = sechdrs[i].sh_info; ++ ++ if (sechdrs[i].sh_type != SHT_REL ++ && sechdrs[i].sh_type != SHT_RELA) ++ continue; ++ ++ /* Not a valid relocation section? */ ++ if (info >= hdr->e_shnum) ++ continue; ++ ++ /* Don't bother with non-allocated sections */ ++ if (!(sechdrs[info].sh_flags & SHF_ALLOC)) ++ continue; ++ ++ /* If it's called *.init*, and we're not init, we're ++ not interested */ ++ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) ++ != is_init) ++ continue; ++ ++ syms = (Elf_Sym *) sechdrs[symindex].sh_addr; ++ if (sechdrs[i].sh_type == SHT_REL) { ++ Elf_Mips_Rel *rel = (void *) sechdrs[i].sh_addr; ++ unsigned int size = sechdrs[i].sh_size / sizeof(*rel); ++ ++ for (j = 0; j < size; ++j) { ++ Elf_Sym *sym; ++ ++ if (ELF_MIPS_R_TYPE(rel[j]) != R_MIPS_26) ++ continue; ++ ++ sym = syms + ELF_MIPS_R_SYM(rel[j]); ++ if (!is_init && sym->st_shndx != SHN_UNDEF) ++ continue; ++ ++ ret += 4 * sizeof(int); ++ } ++ } else { ++ Elf_Mips_Rela *rela = (void *) sechdrs[i].sh_addr; ++ unsigned int size = sechdrs[i].sh_size / sizeof(*rela); ++ ++ for (j = 0; j < size; ++j) { ++ Elf_Sym *sym; ++ ++ if (ELF_MIPS_R_TYPE(rela[j]) != R_MIPS_26) ++ continue; ++ ++ sym = syms + ELF_MIPS_R_SYM(rela[j]); ++ if (!is_init && sym->st_shndx != SHN_UNDEF) ++ continue; ++ ++ ret += 4 * sizeof(int); ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++#ifndef MODULES_VADDR ++static void *alloc_phys(unsigned long size) ++{ ++ unsigned order; ++ struct page *page; ++ struct page *p; ++ ++ size = PAGE_ALIGN(size); ++ order = get_order(size); ++ ++ page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN | ++ __GFP_THISNODE, order); ++ if (!page) ++ return NULL; ++ ++ split_page(page, order); ++ ++ /* mark all pages except for the last one */ ++ for (p = page; p + 1 < page + (size >> PAGE_SHIFT); ++p) ++ set_bit(PG_owner_priv_1, &p->flags); ++ ++ for (p = page + (size >> PAGE_SHIFT); p < page + (1 << order); ++p) ++ __free_page(p); ++ ++ return page_address(page); ++} ++ ++static void free_phys(void *ptr) ++{ ++ struct page *page; ++ bool free; ++ ++ page = virt_to_page(ptr); ++ do { ++ free = test_and_clear_bit(PG_owner_priv_1, &page->flags); ++ __free_page(page); ++ page++; ++ } while (free); ++} ++ ++void *arch_execmem_alloc(enum execmem_type type, ++ size_t size) ++{ ++ void *ptr; ++ ++ ptr = alloc_phys(size); ++ ++ /* If we failed to allocate physically contiguous memory, ++ * fall back to regular vmalloc. The module loader code will ++ * create jump tables to handle long jumps */ ++ if (!ptr) ++ return vmalloc(size); ++ ++ return ptr; ++} ++#endif ++ ++static inline bool is_phys_addr(void *ptr) ++{ ++#ifdef CONFIG_64BIT ++ return (KSEGX((unsigned long)ptr) == CKSEG0); ++#else ++ return (KSEGX(ptr) == KSEG0); ++#endif ++} ++ ++#ifndef MODULES_VADDR ++/* Free memory returned from module_alloc */ ++void arch_execmem_free(void *ptr) ++{ ++ if (is_phys_addr(ptr)) ++ free_phys(ptr); ++ else ++ vfree(ptr); ++} ++#endif ++ ++static void *__module_alloc(int size, bool phys) ++{ ++ void *ptr; ++ ++ if (phys) ++ ptr = kmalloc(size, GFP_KERNEL); ++ else ++ ptr = vmalloc(size); ++ return ptr; ++} ++ ++static void __module_free(void *ptr) ++{ ++ if (is_phys_addr(ptr)) ++ kfree(ptr); ++ else ++ vfree(ptr); ++} ++ ++int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, ++ char *secstrings, struct module *mod) ++{ ++ unsigned int symindex = 0; ++ unsigned int core_size, init_size; ++ int i; ++ ++ mod->arch.phys_plt_offset = 0; ++ mod->arch.virt_plt_offset = 0; ++ mod->arch.phys_plt_tbl = NULL; ++ mod->arch.virt_plt_tbl = NULL; ++ ++ if (IS_ENABLED(CONFIG_64BIT)) ++ return 0; ++ ++ for (i = 1; i < hdr->e_shnum; i++) ++ if (sechdrs[i].sh_type == SHT_SYMTAB) ++ symindex = i; ++ ++ core_size = get_plt_size(hdr, sechdrs, secstrings, symindex, false); ++ init_size = get_plt_size(hdr, sechdrs, secstrings, symindex, true); ++ ++ if ((core_size + init_size) == 0) ++ return 0; ++ ++ mod->arch.phys_plt_tbl = __module_alloc(core_size + init_size, 1); ++ if (!mod->arch.phys_plt_tbl) ++ return -ENOMEM; ++ ++ mod->arch.virt_plt_tbl = __module_alloc(core_size + init_size, 0); ++ if (!mod->arch.virt_plt_tbl) { ++ __module_free(mod->arch.phys_plt_tbl); ++ mod->arch.phys_plt_tbl = NULL; ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ + static void apply_r_mips_32(u32 *location, u32 base, Elf_Addr v) + { + *location = base + v; + } + ++static Elf_Addr add_plt_entry_to(unsigned *plt_offset, ++ void *start, Elf_Addr v) ++{ ++ unsigned *tramp = start + *plt_offset; ++ *plt_offset += 4 * sizeof(int); ++ ++ /* adjust carry for addiu */ ++ if (v & 0x00008000) ++ v += 0x10000; ++ ++ tramp[0] = 0x3c190000 | (v >> 16); /* lui t9, hi16 */ ++ tramp[1] = 0x27390000 | (v & 0xffff); /* addiu t9, t9, lo16 */ ++ tramp[2] = 0x03200008; /* jr t9 */ ++ tramp[3] = 0x00000000; /* nop */ ++ ++ return (Elf_Addr) tramp; ++} ++ ++static Elf_Addr add_plt_entry(struct module *me, void *location, Elf_Addr v) ++{ ++ if (is_phys_addr(location)) ++ return add_plt_entry_to(&me->arch.phys_plt_offset, ++ me->arch.phys_plt_tbl, v); ++ else ++ return add_plt_entry_to(&me->arch.virt_plt_offset, ++ me->arch.virt_plt_tbl, v); ++ ++} ++ + static int apply_r_mips_26(struct module *me, u32 *location, u32 base, + Elf_Addr v) + { ++ u32 ofs = base & 0x03ffffff; ++ + if (v % 4) { + pr_err("module %s: dangerous R_MIPS_26 relocation\n", + me->name); +@@ -45,13 +287,17 @@ static int apply_r_mips_26(struct module + } + + if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { +- pr_err("module %s: relocation overflow\n", +- me->name); +- return -ENOEXEC; ++ v = add_plt_entry(me, location, v + (ofs << 2)); ++ if (!v) { ++ pr_err("module %s: relocation overflow\n", ++ me->name); ++ return -ENOEXEC; ++ } ++ ofs = 0; + } + + *location = (*location & ~0x03ffffff) | +- ((base + (v >> 2)) & 0x03ffffff); ++ ((ofs + (v >> 2)) & 0x03ffffff); + + return 0; + } +@@ -431,9 +677,36 @@ int module_finalize(const Elf_Ehdr *hdr, + list_add(&me->arch.dbe_list, &dbe_list); + spin_unlock_irq(&dbe_lock); + } ++ ++ /* Get rid of the fixup trampoline if we're running the module ++ * from physically mapped address space */ ++ if (me->arch.phys_plt_offset == 0) { ++ __module_free(me->arch.phys_plt_tbl); ++ me->arch.phys_plt_tbl = NULL; ++ } ++ if (me->arch.virt_plt_offset == 0) { ++ __module_free(me->arch.virt_plt_tbl); ++ me->arch.virt_plt_tbl = NULL; ++ } ++ + return 0; + } + ++void module_arch_freeing_init(struct module *mod) ++{ ++ if (mod->state == MODULE_STATE_LIVE) ++ return; ++ ++ if (mod->arch.phys_plt_tbl) { ++ __module_free(mod->arch.phys_plt_tbl); ++ mod->arch.phys_plt_tbl = NULL; ++ } ++ if (mod->arch.virt_plt_tbl) { ++ __module_free(mod->arch.virt_plt_tbl); ++ mod->arch.virt_plt_tbl = NULL; ++ } ++} ++ + void module_arch_cleanup(struct module *mod) + { + spin_lock_irq(&dbe_lock); diff --git a/target/linux/generic/hack-6.12/402-mtd-blktrans-call-add-disks-after-mtd-device.patch b/target/linux/generic/hack-6.12/402-mtd-blktrans-call-add-disks-after-mtd-device.patch new file mode 100644 index 00000000000..d5a68d681bb --- /dev/null +++ b/target/linux/generic/hack-6.12/402-mtd-blktrans-call-add-disks-after-mtd-device.patch @@ -0,0 +1,112 @@ +From 0bccc3722bdd88e8ae995e77ef9f7b77ee4cbdee Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 7 Apr 2021 22:45:54 +0100 +Subject: [PATCH 2/2] mtd: blktrans: call add disks after mtd device +To: linux-mtd@lists.infradead.org +Cc: Vignesh Raghavendra , + Richard Weinberger , + Miquel Raynal , + David Woodhouse + +Calling device_add_disk while holding mtd_table_mutex leads +to deadlock in case part_bits!=0 as block partition parsers +will try to open the newly created disks, trying to acquire +mutex once again. +Move device_add_disk to additional function called after +add partitions of an MTD device have been added and locks +have been released. + +Signed-off-by: Daniel Golle +--- + drivers/mtd/mtd_blkdevs.c | 33 ++++++++++++++++++++++++++------- + drivers/mtd/mtdcore.c | 3 +++ + include/linux/mtd/blktrans.h | 1 + + 3 files changed, 30 insertions(+), 7 deletions(-) + +--- a/drivers/mtd/mtd_blkdevs.c ++++ b/drivers/mtd/mtd_blkdevs.c +@@ -379,19 +379,8 @@ int add_mtd_blktrans_dev(struct mtd_blkt + if (new->readonly) + set_disk_ro(gd, 1); + +- ret = device_add_disk(&new->mtd->dev, gd, NULL); +- if (ret) +- goto out_cleanup_disk; +- +- if (new->disk_attributes) { +- ret = sysfs_create_group(&disk_to_dev(gd)->kobj, +- new->disk_attributes); +- WARN_ON(ret); +- } + return 0; + +-out_cleanup_disk: +- put_disk(new->disk); + out_free_tag_set: + blk_mq_free_tag_set(new->tag_set); + out_kfree_tag_set: +@@ -401,6 +390,35 @@ out_list_del: + return ret; + } + ++void register_mtd_blktrans_devs(void) ++{ ++ struct mtd_blktrans_ops *tr; ++ struct mtd_blktrans_dev *dev, *next; ++ int ret; ++ ++ list_for_each_entry(tr, &blktrans_majors, list) { ++ list_for_each_entry_safe(dev, next, &tr->devs, list) { ++ if (disk_live(dev->disk)) ++ continue; ++ ++ ret = device_add_disk(&dev->mtd->dev, dev->disk, NULL); ++ if (ret) ++ goto out_cleanup_disk; ++ ++ if (dev->disk_attributes) { ++ ret = sysfs_create_group(&disk_to_dev(dev->disk)->kobj, ++ dev->disk_attributes); ++ WARN_ON(ret); ++ } ++ } ++ } ++ ++ return; ++ ++out_cleanup_disk: ++ put_disk(dev->disk); ++} ++ + int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) + { + unsigned long flags; +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -34,6 +34,7 @@ + + #include + #include ++#include + + #include "mtdcore.h" + +@@ -1109,6 +1110,8 @@ int mtd_device_parse_register(struct mtd + register_reboot_notifier(&mtd->reboot_notifier); + } + ++ register_mtd_blktrans_devs(); ++ + out: + if (ret) { + nvmem_unregister(mtd->otp_user_nvmem); +--- a/include/linux/mtd/blktrans.h ++++ b/include/linux/mtd/blktrans.h +@@ -76,6 +76,7 @@ extern int deregister_mtd_blktrans(struc + extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); + extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); + extern int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev); ++extern void register_mtd_blktrans_devs(void); + + /** + * module_mtd_blktrans() - Helper macro for registering a mtd blktrans driver diff --git a/target/linux/generic/hack-6.12/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch b/target/linux/generic/hack-6.12/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch new file mode 100644 index 00000000000..1b17c2fe882 --- /dev/null +++ b/target/linux/generic/hack-6.12/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch @@ -0,0 +1,24 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 7 Nov 2022 23:48:24 +0100 +Subject: [PATCH] mtd: support OpenWrt's MTD_ROOTFS_ROOT_DEV +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This allows setting ROOT_DEV to MTD partition named "rootfs". + +Signed-off-by: Rafał Miłecki +--- + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -780,7 +780,8 @@ int add_mtd_device(struct mtd_info *mtd) + + mutex_unlock(&mtd_table_mutex); + +- if (of_property_read_bool(mtd_get_of_node(mtd), "linux,rootfs")) { ++ if (of_property_read_bool(mtd_get_of_node(mtd), "linux,rootfs") || ++ (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && !strcmp(mtd->name, "rootfs") && ROOT_DEV == 0)) { + if (IS_BUILTIN(CONFIG_MTD)) { + pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name); + ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index); diff --git a/target/linux/generic/hack-6.12/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch b/target/linux/generic/hack-6.12/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch new file mode 100644 index 00000000000..190fecc1a0e --- /dev/null +++ b/target/linux/generic/hack-6.12/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch @@ -0,0 +1,120 @@ +From 6fa9e3678eb002246df1280322b6a024853950a5 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Mon, 11 Oct 2021 00:53:14 +0200 +Subject: [PATCH] drivers: mtd: parsers: add nvmem support to cmdlinepart + +Assuming cmdlinepart is only one level deep partition scheme and that +static partition are also defined in DTS, we can assign an of_node for +partition declared from bootargs. cmdlinepart have priority than +fiexed-partition parser so in this specific case the parser doesn't +assign an of_node. Fix this by searching a defined of_node using a +similar fixed_partition parser and if a partition is found with the same +label, check that it has the same offset and size and return the DT +of_node to correctly use NVMEM cells. + +Signed-off-by: Ansuel Smith +--- + drivers/mtd/parsers/cmdlinepart.c | 71 +++++++++++++++++++++++++++++++ + 1 file changed, 71 insertions(+) + +--- a/drivers/mtd/parsers/cmdlinepart.c ++++ b/drivers/mtd/parsers/cmdlinepart.c +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + + /* special size referring to all the remaining space in a partition */ + #define SIZE_REMAINING ULLONG_MAX +@@ -315,6 +316,68 @@ static int mtdpart_setup_real(char *s) + return 0; + } + ++static int search_fixed_partition(struct mtd_info *master, ++ struct mtd_partition *target_part, ++ struct mtd_partition *fixed_part) ++{ ++ struct device_node *mtd_node; ++ struct device_node *ofpart_node; ++ struct device_node *pp; ++ struct mtd_partition part; ++ const char *partname; ++ ++ mtd_node = mtd_get_of_node(master); ++ if (!mtd_node) ++ return -EINVAL; ++ ++ ofpart_node = of_get_child_by_name(mtd_node, "partitions"); ++ ++ for_each_child_of_node(ofpart_node, pp) { ++ const __be32 *reg; ++ int len; ++ int a_cells, s_cells; ++ ++ reg = of_get_property(pp, "reg", &len); ++ if (!reg) { ++ pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n", ++ master->name, pp, ++ mtd_node); ++ continue; ++ } ++ ++ a_cells = of_n_addr_cells(pp); ++ s_cells = of_n_size_cells(pp); ++ if (len / 4 != a_cells + s_cells) { ++ pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", ++ master->name, pp, ++ mtd_node); ++ continue; ++ } ++ ++ part.offset = of_read_number(reg, a_cells); ++ part.size = of_read_number(reg + a_cells, s_cells); ++ part.of_node = pp; ++ ++ partname = of_get_property(pp, "label", &len); ++ if (!partname) ++ partname = of_get_property(pp, "name", &len); ++ part.name = partname; ++ ++ if (!strncmp(target_part->name, part.name, len)) { ++ if (part.offset != target_part->offset) ++ return -EINVAL; ++ ++ if (part.size != target_part->size) ++ return -EINVAL; ++ ++ memcpy(fixed_part, &part, sizeof(struct mtd_partition)); ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ + /* + * Main function to be called from the MTD mapping driver/device to + * obtain the partitioning information. At this point the command line +@@ -330,6 +393,7 @@ static int parse_cmdline_partitions(stru + int i, err; + struct cmdline_mtd_partition *part; + const char *mtd_id = master->name; ++ struct mtd_partition fixed_part; + + /* parse command line */ + if (!cmdline_parsed) { +@@ -374,6 +438,13 @@ static int parse_cmdline_partitions(stru + sizeof(*part->parts) * (part->num_parts - i)); + i--; + } ++ ++ err = search_fixed_partition(master, &part->parts[i], &fixed_part); ++ if (!err) { ++ part->parts[i].of_node = fixed_part.of_node; ++ pr_info("Found partition defined in DT for %s. Assigning OF node to support nvmem.", ++ part->parts[i].name); ++ } + } + + *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts, diff --git a/target/linux/generic/hack-6.12/430-mtk-bmt-support.patch b/target/linux/generic/hack-6.12/430-mtk-bmt-support.patch new file mode 100644 index 00000000000..8d1c16503f3 --- /dev/null +++ b/target/linux/generic/hack-6.12/430-mtk-bmt-support.patch @@ -0,0 +1,33 @@ +From ac84397efb3b3868c71c10ad7521161773228a17 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:41:44 +0200 +Subject: [PATCH] mtd/nand: add MediaTek NAND bad block managment table + +--- + drivers/mtd/nand/Kconfig | 4 ++++ + drivers/mtd/nand/Makefile | 1 + + 2 files changed, 5 insertions(+) + +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -46,6 +46,10 @@ config MTD_NAND_ECC_SW_BCH + ECC codes. They are used with NAND devices requiring more than 1 bit + of error correction. + ++config MTD_NAND_MTK_BMT ++ bool "Support MediaTek NAND Bad-block Management Table" ++ default n ++ + config MTD_NAND_ECC_MXIC + bool "Macronix external hardware ECC engine" + depends on HAS_IOMEM +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -3,6 +3,7 @@ + nandcore-objs := core.o bbt.o + obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o + obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o ++obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o + obj-$(CONFIG_SPI_QPIC_SNAND) += qpic_common.o + obj-$(CONFIG_MTD_NAND_QCOM) += qpic_common.o + obj-y += onenand/ diff --git a/target/linux/generic/hack-6.12/499-LEGACY-block-partitions-populate-fwnode.patch b/target/linux/generic/hack-6.12/499-LEGACY-block-partitions-populate-fwnode.patch new file mode 100644 index 00000000000..f7ec9efba7c --- /dev/null +++ b/target/linux/generic/hack-6.12/499-LEGACY-block-partitions-populate-fwnode.patch @@ -0,0 +1,130 @@ +From: Daniel Golle +Subject: [PATCH] LEGACY block: partitions: populate fwnode + +Assign matching firmware nodes to block partitions in order to allow +them to be referenced e.g. as NVMEM providers. + +REMOVE THIS PATCH ONCE ALL TARGETS ARE USING LINUX 6.12 AND ALL BOARDS +HAVE MIGRATED TO UPSTREAM DT BINDINGS. + +Signed-off-by: Daniel Golle +--- a/block/partitions/core.c ++++ b/block/partitions/core.c +@@ -11,6 +11,8 @@ + #include + #include + #include ++#include ++ + #include "check.h" + + static int (*const check_part[])(struct parsed_partitions *) = { +@@ -285,6 +287,74 @@ static ssize_t whole_disk_show(struct de + } + static const DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL); + ++static bool part_meta_match(const char *attr, const char *member, size_t length) ++{ ++ /* check if length of attr exceeds specified maximum length */ ++ if (strnlen(attr, length) == length) ++ return false; ++ ++ /* return true if strings match */ ++ return !strncmp(attr, member, length); ++} ++ ++static struct fwnode_handle *find_partition_fwnode(struct block_device *bdev) ++{ ++ struct fwnode_handle *fw_parts, *fw_part; ++ struct device *ddev = disk_to_dev(bdev->bd_disk); ++ const char *partname, *uuid; ++ u32 partno; ++ bool got_uuid, got_partname, got_partno; ++ ++ fw_parts = device_get_named_child_node(ddev, "partitions"); ++ if (!fw_parts) ++ return NULL; ++ ++ fwnode_for_each_child_node(fw_parts, fw_part) { ++ got_uuid = false; ++ got_partname = false; ++ got_partno = false; ++ /* ++ * In case 'uuid' is defined in the partitions firmware node ++ * require partition meta info being present and the specified ++ * uuid to match. ++ */ ++ got_uuid = !fwnode_property_read_string(fw_part, "uuid", &uuid); ++ if (got_uuid && (!bdev->bd_meta_info || ++ !part_meta_match(uuid, bdev->bd_meta_info->uuid, ++ PARTITION_META_INFO_UUIDLTH))) ++ continue; ++ ++ /* ++ * In case 'partname' is defined in the partitions firmware node ++ * require partition meta info being present and the specified ++ * volname to match. ++ */ ++ got_partname = !fwnode_property_read_string(fw_part, "partname", ++ &partname); ++ if (got_partname && (!bdev->bd_meta_info || ++ !part_meta_match(partname, ++ bdev->bd_meta_info->volname, ++ PARTITION_META_INFO_VOLNAMELTH))) ++ continue; ++ ++ /* ++ * In case 'partno' is defined in the partitions firmware node ++ * the specified partno needs to match. ++ */ ++ got_partno = !fwnode_property_read_u32(fw_part, "partno", &partno); ++ if (got_partno && bdev_partno(bdev) != partno) ++ continue; ++ ++ /* Skip if no matching criteria is present in firmware node */ ++ if (!got_uuid && !got_partname && !got_partno) ++ continue; ++ ++ return fw_part; ++ } ++ ++ return NULL; ++} ++ + /* + * Must be called either with open_mutex held, before a disk can be opened or + * after all disk users are gone. +@@ -361,6 +431,9 @@ static struct block_device *add_partitio + goto out_put; + } + ++ if (!pdev->fwnode && !pdev->of_node) ++ device_set_node(pdev, find_partition_fwnode(bdev)); ++ + /* delay uevent until 'holders' subdir is created */ + dev_set_uevent_suppress(pdev, 1); + err = device_add(pdev); +--- a/drivers/mmc/core/bus.c ++++ b/drivers/mmc/core/bus.c +@@ -368,6 +368,8 @@ int mmc_add_card(struct mmc_card *card) + + mmc_add_card_debugfs(card); + card->dev.of_node = mmc_of_find_child_device(card->host, 0); ++ if (card->dev.of_node && !card->dev.fwnode) ++ card->dev.fwnode = &card->dev.of_node->fwnode; + + device_enable_async_suspend(&card->dev); + +--- a/drivers/mmc/core/block.c ++++ b/drivers/mmc/core/block.c +@@ -2679,6 +2679,10 @@ static struct mmc_blk_data *mmc_blk_allo + if (area_type == MMC_BLK_DATA_AREA_MAIN) + dev_set_drvdata(&card->dev, md); + disk_fwnode = mmc_blk_get_partitions_node(parent, subname); ++ if (!disk_fwnode) ++ disk_fwnode = device_get_named_child_node(subname ? md->parent->parent : ++ md->parent, ++ subname ? subname : "block"); + ret = add_disk_fwnode(md->parent, md->disk, mmc_disk_attr_groups, + disk_fwnode); + if (ret) diff --git a/target/linux/generic/hack-6.12/600-net-enable-fraglist-GRO-by-default.patch b/target/linux/generic/hack-6.12/600-net-enable-fraglist-GRO-by-default.patch new file mode 100644 index 00000000000..feed2cd2e0f --- /dev/null +++ b/target/linux/generic/hack-6.12/600-net-enable-fraglist-GRO-by-default.patch @@ -0,0 +1,24 @@ +From: Felix Fietkau +Date: Tue, 23 Apr 2024 12:35:21 +0200 +Subject: [PATCH] net: enable fraglist GRO by default + +This can significantly improve performance for packet forwarding/bridging + +Signed-off-by: Felix Fietkau +--- + +--- a/include/linux/netdev_features.h ++++ b/include/linux/netdev_features.h +@@ -235,10 +235,10 @@ static inline int find_next_netdev_featu + #define NETIF_F_UPPER_DISABLES NETIF_F_LRO + + /* changeable features with no special hardware requirements */ +-#define NETIF_F_SOFT_FEATURES (NETIF_F_GSO | NETIF_F_GRO) ++#define NETIF_F_SOFT_FEATURES (NETIF_F_GSO | NETIF_F_GRO | NETIF_F_GRO_FRAGLIST) + + /* Changeable features with no special hardware requirements that defaults to off. */ +-#define NETIF_F_SOFT_FEATURES_OFF (NETIF_F_GRO_FRAGLIST | NETIF_F_GRO_UDP_FWD) ++#define NETIF_F_SOFT_FEATURES_OFF (NETIF_F_GRO_UDP_FWD) + + #define NETIF_F_VLAN_FEATURES (NETIF_F_HW_VLAN_CTAG_FILTER | \ + NETIF_F_HW_VLAN_CTAG_RX | \ diff --git a/target/linux/generic/hack-6.12/610-net-page_pool-try-to-free-deferred-skbs-while-waitin.patch b/target/linux/generic/hack-6.12/610-net-page_pool-try-to-free-deferred-skbs-while-waitin.patch new file mode 100644 index 00000000000..7f79e041df1 --- /dev/null +++ b/target/linux/generic/hack-6.12/610-net-page_pool-try-to-free-deferred-skbs-while-waitin.patch @@ -0,0 +1,51 @@ +From: Felix Fietkau +Date: Fri, 3 Jan 2025 19:29:00 +0100 +Subject: [PATCH] net: page_pool: try to free deferred skbs while waiting for + pool release + +The NAPI defer list can accumulate no longer used skbs, which can be reused +during alloc. If this happens on a CPU that otherwise does not do any +rx softirq processing, skbs can be held indefinitely, causing warnings +on releasing page pools. +Deal with this by scheduling rx softirq on all CPUs. + +Patch by Lorenzo Bianconi + +Signed-off-by: Felix Fietkau +--- + +--- a/net/core/page_pool.c ++++ b/net/core/page_pool.c +@@ -1155,8 +1155,9 @@ static void page_pool_release_retry(stru + { + struct delayed_work *dwq = to_delayed_work(wq); + struct page_pool *pool = container_of(dwq, typeof(*pool), release_dw); ++ unsigned long flags; + void *netdev; +- int inflight; ++ int cpu, inflight; + + inflight = page_pool_release(pool); + /* In rare cases, a driver bug may cause inflight to go negative. +@@ -1168,6 +1169,21 @@ static void page_pool_release_retry(stru + if (inflight <= 0) + return; + ++ /* Run NET_RX_SOFTIRQ in order to free pending skbs in softnet_data ++ * defer_list that can stay in the list until we have enough queued ++ * traffic. ++ */ ++ local_irq_save(flags); ++ for_each_online_cpu(cpu) { ++ struct softnet_data *sd = &per_cpu(softnet_data, cpu); ++ ++ if (cpu == raw_smp_processor_id()) ++ raise_softirq_irqoff(NET_RX_SOFTIRQ); ++ else if (!cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) ++ smp_call_function_single_async(cpu, &sd->defer_csd); ++ } ++ local_irq_restore(flags); ++ + /* Periodic warning for page pools the user can't see */ + netdev = READ_ONCE(pool->slow.netdev); + if (time_after_eq(jiffies, pool->defer_warn) && diff --git a/target/linux/generic/hack-6.12/645-netfilter-connmark-introduce-set-dscpmark.patch b/target/linux/generic/hack-6.12/645-netfilter-connmark-introduce-set-dscpmark.patch new file mode 100644 index 00000000000..bb802857d6b --- /dev/null +++ b/target/linux/generic/hack-6.12/645-netfilter-connmark-introduce-set-dscpmark.patch @@ -0,0 +1,231 @@ +From eda40b8c8c82e0f2789d6bc8bf63846dce2e8f32 Mon Sep 17 00:00:00 2001 +From: Kevin Darbyshire-Bryant +Date: Sat, 23 Mar 2019 09:29:49 +0000 +Subject: [PATCH] netfilter: connmark: introduce set-dscpmark + +set-dscpmark is a method of storing the DSCP of an ip packet into +conntrack mark. In combination with a suitable tc filter action +(act_ctinfo) DSCP values are able to be stored in the mark on egress and +restored on ingress across links that otherwise alter or bleach DSCP. + +This is useful for qdiscs such as CAKE which are able to shape according +to policies based on DSCP. + +Ingress classification is traditionally a challenging task since +iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT +lookups, hence are unable to see internal IPv4 addresses as used on the +typical home masquerading gateway. + +x_tables CONNMARK set-dscpmark target solves the problem of storing the +DSCP to the conntrack mark in a way suitable for the new act_ctinfo tc +action to restore. + +The set-dscpmark option accepts 2 parameters, a 32bit 'dscpmask' and a +32bit 'statemask'. The dscp mask must be 6 contiguous bits and +represents the area where the DSCP will be stored in the connmark. The +state mask is a minimum 1 bit length mask that must not overlap with the +dscpmask. It represents a flag which is set when the DSCP has been +stored in the conntrack mark. This is useful to implement a 'one shot' +iptables based classification where the 'complicated' iptables rules are +only run once to classify the connection on initial (egress) packet and +subsequent packets are all marked/restored with the same DSCP. A state +mask of zero disables the setting of a status bit/s. + +example syntax with a suitably modified iptables user space application: + +iptables -A QOS_MARK_eth0 -t mangle -j CONNMARK --set-dscpmark 0xfc000000/0x01000000 + +Would store the DSCP in the top 6 bits of the 32bit mark field, and use +the LSB of the top byte as the 'DSCP has been stored' marker. + +|----0xFC----conntrack mark----000000---| +| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0| +| DSCP | unused | flag |unused | +|-----------------------0x01---000000---| + ^ ^ + | | + ---| Conditional flag + | set this when dscp +|-ip diffserv-| stored in mark +| 6 bits | +|-------------| + +an identically configured tc action to restore looks like: + +tc filter show dev eth0 ingress +filter parent ffff: protocol all pref 10 u32 chain 0 +filter parent ffff: protocol all pref 10 u32 chain 0 fh 800: ht divisor 1 +filter parent ffff: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1: not_in_hw + match 00000000/00000000 at 0 + action order 1: ctinfo zone 0 pipe + index 2 ref 1 bind 1 dscp 0xfc000000/0x1000000 + + action order 2: mirred (Egress Redirect to device ifb4eth0) stolen + index 1 ref 1 bind 1 + +|----0xFC----conntrack mark----000000---| +| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0| +| DSCP | unused | flag |unused | +|-----------------------0x01---000000---| + | | + | | + ---| Conditional flag + v only restore if set +|-ip diffserv-| +| 6 bits | +|-------------| + +Signed-off-by: Kevin Darbyshire-Bryant +--- + include/uapi/linux/netfilter/xt_connmark.h | 10 ++++ + net/netfilter/xt_connmark.c | 55 ++++++++++++++++++---- + 2 files changed, 57 insertions(+), 8 deletions(-) + +--- a/include/uapi/linux/netfilter/xt_connmark.h ++++ b/include/uapi/linux/netfilter/xt_connmark.h +@@ -15,6 +15,11 @@ enum { + }; + + enum { ++ XT_CONNMARK_VALUE = (1 << 0), ++ XT_CONNMARK_DSCP = (1 << 1) ++}; ++ ++enum { + D_SHIFT_LEFT = 0, + D_SHIFT_RIGHT, + }; +@@ -29,6 +34,11 @@ struct xt_connmark_tginfo2 { + __u8 shift_dir, shift_bits, mode; + }; + ++struct xt_connmark_tginfo3 { ++ __u32 ctmark, ctmask, nfmask; ++ __u8 shift_dir, shift_bits, mode, func; ++}; ++ + struct xt_connmark_mtinfo1 { + __u32 mark, mask; + __u8 invert; +--- a/net/netfilter/xt_connmark.c ++++ b/net/netfilter/xt_connmark.c +@@ -24,13 +24,13 @@ MODULE_ALIAS("ipt_connmark"); + MODULE_ALIAS("ip6t_connmark"); + + static unsigned int +-connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info) ++connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo3 *info) + { + enum ip_conntrack_info ctinfo; + u_int32_t new_targetmark; + struct nf_conn *ct; + u_int32_t newmark; +- u_int32_t oldmark; ++ u_int8_t dscp; + + ct = nf_ct_get(skb, &ctinfo); + if (ct == NULL) +@@ -38,13 +38,24 @@ connmark_tg_shift(struct sk_buff *skb, c + + switch (info->mode) { + case XT_CONNMARK_SET: +- oldmark = READ_ONCE(ct->mark); +- newmark = (oldmark & ~info->ctmask) ^ info->ctmark; +- if (info->shift_dir == D_SHIFT_RIGHT) +- newmark >>= info->shift_bits; +- else +- newmark <<= info->shift_bits; ++ newmark = READ_ONCE(ct->mark); ++ if (info->func & XT_CONNMARK_VALUE) { ++ newmark = (newmark & ~info->ctmask) ^ info->ctmark; ++ if (info->shift_dir == D_SHIFT_RIGHT) ++ newmark >>= info->shift_bits; ++ else ++ newmark <<= info->shift_bits; ++ } else if (info->func & XT_CONNMARK_DSCP) { ++ if (skb->protocol == htons(ETH_P_IP)) ++ dscp = ipv4_get_dsfield(ip_hdr(skb)) >> 2; ++ else if (skb->protocol == htons(ETH_P_IPV6)) ++ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; ++ else /* protocol doesn't have diffserv */ ++ break; + ++ newmark = (newmark & ~info->ctmark) | ++ (info->ctmask | (dscp << info->shift_bits)); ++ } + if (READ_ONCE(ct->mark) != newmark) { + WRITE_ONCE(ct->mark, newmark); + nf_conntrack_event_cache(IPCT_MARK, ct); +@@ -83,20 +94,36 @@ static unsigned int + connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) + { + const struct xt_connmark_tginfo1 *info = par->targinfo; +- const struct xt_connmark_tginfo2 info2 = { ++ const struct xt_connmark_tginfo3 info3 = { + .ctmark = info->ctmark, + .ctmask = info->ctmask, + .nfmask = info->nfmask, + .mode = info->mode, ++ .func = XT_CONNMARK_VALUE + }; + +- return connmark_tg_shift(skb, &info2); ++ return connmark_tg_shift(skb, &info3); + } + + static unsigned int + connmark_tg_v2(struct sk_buff *skb, const struct xt_action_param *par) + { + const struct xt_connmark_tginfo2 *info = par->targinfo; ++ const struct xt_connmark_tginfo3 info3 = { ++ .ctmark = info->ctmark, ++ .ctmask = info->ctmask, ++ .nfmask = info->nfmask, ++ .mode = info->mode, ++ .func = XT_CONNMARK_VALUE ++ }; ++ ++ return connmark_tg_shift(skb, &info3); ++} ++ ++static unsigned int ++connmark_tg_v3(struct sk_buff *skb, const struct xt_action_param *par) ++{ ++ const struct xt_connmark_tginfo3 *info = par->targinfo; + + return connmark_tg_shift(skb, info); + } +@@ -168,6 +195,16 @@ static struct xt_target connmark_tg_reg[ + .destroy = connmark_tg_destroy, + .me = THIS_MODULE, + }, ++ { ++ .name = "CONNMARK", ++ .revision = 3, ++ .family = NFPROTO_IPV4, ++ .checkentry = connmark_tg_check, ++ .target = connmark_tg_v3, ++ .targetsize = sizeof(struct xt_connmark_tginfo3), ++ .destroy = connmark_tg_destroy, ++ .me = THIS_MODULE, ++ }, + #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) + { + .name = "CONNMARK", +@@ -189,6 +226,16 @@ static struct xt_target connmark_tg_reg[ + .destroy = connmark_tg_destroy, + .me = THIS_MODULE, + }, ++ { ++ .name = "CONNMARK", ++ .revision = 3, ++ .family = NFPROTO_IPV6, ++ .checkentry = connmark_tg_check, ++ .target = connmark_tg_v3, ++ .targetsize = sizeof(struct xt_connmark_tginfo3), ++ .destroy = connmark_tg_destroy, ++ .me = THIS_MODULE, ++ }, + #endif + }; + diff --git a/target/linux/generic/hack-6.12/650-netfilter-add-xt_FLOWOFFLOAD-target.patch b/target/linux/generic/hack-6.12/650-netfilter-add-xt_FLOWOFFLOAD-target.patch new file mode 100644 index 00000000000..53093daa36a --- /dev/null +++ b/target/linux/generic/hack-6.12/650-netfilter-add-xt_FLOWOFFLOAD-target.patch @@ -0,0 +1,812 @@ +From: Felix Fietkau +Date: Tue, 20 Feb 2018 15:56:02 +0100 +Subject: [PATCH] netfilter: add xt_FLOWOFFLOAD target + +Signed-off-by: Felix Fietkau +--- + create mode 100644 net/netfilter/xt_OFFLOAD.c + +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -729,7 +729,6 @@ config NF_FLOW_TABLE + tristate "Netfilter flow table module" + depends on NETFILTER_INGRESS + depends on NF_CONNTRACK +- depends on NF_TABLES + help + This option adds the flow table core infrastructure. + +@@ -1025,6 +1024,15 @@ config NETFILTER_XT_TARGET_NOTRACK + depends on NETFILTER_ADVANCED + select NETFILTER_XT_TARGET_CT + ++config NETFILTER_XT_TARGET_FLOWOFFLOAD ++ tristate '"FLOWOFFLOAD" target support' ++ depends on NF_FLOW_TABLE ++ depends on NETFILTER_INGRESS ++ help ++ This option adds a `FLOWOFFLOAD' target, which uses the nf_flow_offload ++ module to speed up processing of packets by bypassing the usual ++ netfilter chains ++ + config NETFILTER_XT_TARGET_RATEEST + tristate '"RATEEST" target support' + depends on NETFILTER_ADVANCED +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -168,6 +168,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF + obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o + obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o ++obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o + obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o + obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o +--- /dev/null ++++ b/net/netfilter/xt_FLOWOFFLOAD.c +@@ -0,0 +1,703 @@ ++/* ++ * Copyright (C) 2018-2021 Felix Fietkau ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct xt_flowoffload_hook { ++ struct hlist_node list; ++ struct nf_hook_ops ops; ++ struct net *net; ++ bool registered; ++ bool used; ++}; ++ ++struct xt_flowoffload_table { ++ struct nf_flowtable ft; ++ struct hlist_head hooks; ++ struct delayed_work work; ++}; ++ ++struct nf_forward_info { ++ const struct net_device *indev; ++ const struct net_device *outdev; ++ const struct net_device *hw_outdev; ++ struct id { ++ __u16 id; ++ __be16 proto; ++ } encap[NF_FLOW_TABLE_ENCAP_MAX]; ++ u8 num_encaps; ++ u8 ingress_vlans; ++ u8 h_source[ETH_ALEN]; ++ u8 h_dest[ETH_ALEN]; ++ enum flow_offload_xmit_type xmit_type; ++}; ++ ++static DEFINE_SPINLOCK(hooks_lock); ++ ++struct xt_flowoffload_table flowtable[2]; ++ ++static unsigned int ++xt_flowoffload_net_hook(void *priv, struct sk_buff *skb, ++ const struct nf_hook_state *state) ++{ ++ struct vlan_ethhdr *veth; ++ __be16 proto; ++ ++ switch (skb->protocol) { ++ case htons(ETH_P_8021Q): ++ veth = (struct vlan_ethhdr *)skb_mac_header(skb); ++ proto = veth->h_vlan_encapsulated_proto; ++ break; ++ case htons(ETH_P_PPP_SES): ++ if (!nf_flow_pppoe_proto(skb, &proto)) ++ return NF_ACCEPT; ++ break; ++ default: ++ proto = skb->protocol; ++ break; ++ } ++ ++ switch (proto) { ++ case htons(ETH_P_IP): ++ return nf_flow_offload_ip_hook(priv, skb, state); ++ case htons(ETH_P_IPV6): ++ return nf_flow_offload_ipv6_hook(priv, skb, state); ++ } ++ ++ return NF_ACCEPT; ++} ++ ++static int ++xt_flowoffload_create_hook(struct xt_flowoffload_table *table, ++ struct net_device *dev) ++{ ++ struct xt_flowoffload_hook *hook; ++ struct nf_hook_ops *ops; ++ ++ hook = kzalloc(sizeof(*hook), GFP_ATOMIC); ++ if (!hook) ++ return -ENOMEM; ++ ++ ops = &hook->ops; ++ ops->pf = NFPROTO_NETDEV; ++ ops->hooknum = NF_NETDEV_INGRESS; ++ ops->priority = 10; ++ ops->priv = &table->ft; ++ ops->hook = xt_flowoffload_net_hook; ++ ops->dev = dev; ++ ++ hlist_add_head(&hook->list, &table->hooks); ++ mod_delayed_work(system_power_efficient_wq, &table->work, 0); ++ ++ return 0; ++} ++ ++static struct xt_flowoffload_hook * ++flow_offload_lookup_hook(struct xt_flowoffload_table *table, ++ struct net_device *dev) ++{ ++ struct xt_flowoffload_hook *hook; ++ ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->ops.dev == dev) ++ return hook; ++ } ++ ++ return NULL; ++} ++ ++static void ++xt_flowoffload_check_device(struct xt_flowoffload_table *table, ++ struct net_device *dev) ++{ ++ struct xt_flowoffload_hook *hook; ++ ++ if (!dev) ++ return; ++ ++ spin_lock_bh(&hooks_lock); ++ hook = flow_offload_lookup_hook(table, dev); ++ if (hook) ++ hook->used = true; ++ else ++ xt_flowoffload_create_hook(table, dev); ++ spin_unlock_bh(&hooks_lock); ++} ++ ++static void ++xt_flowoffload_register_hooks(struct xt_flowoffload_table *table) ++{ ++ struct xt_flowoffload_hook *hook; ++ ++restart: ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->registered) ++ continue; ++ ++ hook->registered = true; ++ hook->net = dev_net(hook->ops.dev); ++ spin_unlock_bh(&hooks_lock); ++ nf_register_net_hook(hook->net, &hook->ops); ++ if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD) ++ table->ft.type->setup(&table->ft, hook->ops.dev, ++ FLOW_BLOCK_BIND); ++ spin_lock_bh(&hooks_lock); ++ goto restart; ++ } ++ ++} ++ ++static bool ++xt_flowoffload_cleanup_hooks(struct xt_flowoffload_table *table) ++{ ++ struct xt_flowoffload_hook *hook; ++ bool active = false; ++ ++restart: ++ spin_lock_bh(&hooks_lock); ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->used || !hook->registered) { ++ active = true; ++ continue; ++ } ++ ++ hlist_del(&hook->list); ++ spin_unlock_bh(&hooks_lock); ++ if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD) ++ table->ft.type->setup(&table->ft, hook->ops.dev, ++ FLOW_BLOCK_UNBIND); ++ nf_unregister_net_hook(hook->net, &hook->ops); ++ kfree(hook); ++ goto restart; ++ } ++ spin_unlock_bh(&hooks_lock); ++ ++ return active; ++} ++ ++static void ++xt_flowoffload_check_hook(struct nf_flowtable *flowtable, ++ struct flow_offload *flow, void *data) ++{ ++ struct xt_flowoffload_table *table; ++ struct flow_offload_tuple *tuple0 = &flow->tuplehash[0].tuple; ++ struct flow_offload_tuple *tuple1 = &flow->tuplehash[1].tuple; ++ struct xt_flowoffload_hook *hook; ++ ++ table = container_of(flowtable, struct xt_flowoffload_table, ft); ++ ++ spin_lock_bh(&hooks_lock); ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->ops.dev->ifindex != tuple0->iifidx && ++ hook->ops.dev->ifindex != tuple1->iifidx) ++ continue; ++ ++ hook->used = true; ++ } ++ spin_unlock_bh(&hooks_lock); ++} ++ ++static void ++xt_flowoffload_hook_work(struct work_struct *work) ++{ ++ struct xt_flowoffload_table *table; ++ struct xt_flowoffload_hook *hook; ++ int err; ++ ++ table = container_of(work, struct xt_flowoffload_table, work.work); ++ ++ spin_lock_bh(&hooks_lock); ++ xt_flowoffload_register_hooks(table); ++ hlist_for_each_entry(hook, &table->hooks, list) ++ hook->used = false; ++ spin_unlock_bh(&hooks_lock); ++ ++ err = nf_flow_table_iterate(&table->ft, xt_flowoffload_check_hook, ++ NULL); ++ if (err && err != -EAGAIN) ++ goto out; ++ ++ if (!xt_flowoffload_cleanup_hooks(table)) ++ return; ++ ++out: ++ queue_delayed_work(system_power_efficient_wq, &table->work, HZ); ++} ++ ++static bool ++xt_flowoffload_skip(struct sk_buff *skb, int family) ++{ ++ if (skb_sec_path(skb)) ++ return true; ++ ++ if (family == NFPROTO_IPV4) { ++ const struct ip_options *opt = &(IPCB(skb)->opt); ++ ++ if (unlikely(opt->optlen)) ++ return true; ++ } ++ ++ return false; ++} ++ ++static enum flow_offload_xmit_type nf_xmit_type(struct dst_entry *dst) ++{ ++ if (dst_xfrm(dst)) ++ return FLOW_OFFLOAD_XMIT_XFRM; ++ ++ return FLOW_OFFLOAD_XMIT_NEIGH; ++} ++ ++static void nf_default_forward_path(struct nf_flow_route *route, ++ struct dst_entry *dst_cache, ++ enum ip_conntrack_dir dir, ++ struct net_device **dev) ++{ ++ dev[!dir] = dst_cache->dev; ++ route->tuple[!dir].in.ifindex = dst_cache->dev->ifindex; ++ route->tuple[dir].dst = dst_cache; ++ route->tuple[dir].xmit_type = nf_xmit_type(dst_cache); ++} ++ ++static bool nf_is_valid_ether_device(const struct net_device *dev) ++{ ++ if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER || ++ dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr)) ++ return false; ++ ++ return true; ++} ++ ++static void nf_dev_path_info(const struct net_device_path_stack *stack, ++ struct nf_forward_info *info, ++ unsigned char *ha) ++{ ++ const struct net_device_path *path; ++ int i; ++ ++ memcpy(info->h_dest, ha, ETH_ALEN); ++ ++ for (i = 0; i < stack->num_paths; i++) { ++ path = &stack->path[i]; ++ switch (path->type) { ++ case DEV_PATH_ETHERNET: ++ case DEV_PATH_DSA: ++ case DEV_PATH_VLAN: ++ case DEV_PATH_PPPOE: ++ info->indev = path->dev; ++ if (is_zero_ether_addr(info->h_source)) ++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); ++ ++ if (path->type == DEV_PATH_ETHERNET) ++ break; ++ if (path->type == DEV_PATH_DSA) { ++ i = stack->num_paths; ++ break; ++ } ++ ++ /* DEV_PATH_VLAN and DEV_PATH_PPPOE */ ++ if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { ++ info->indev = NULL; ++ break; ++ } ++ if (!info->outdev) ++ info->outdev = path->dev; ++ info->encap[info->num_encaps].id = path->encap.id; ++ info->encap[info->num_encaps].proto = path->encap.proto; ++ info->num_encaps++; ++ if (path->type == DEV_PATH_PPPOE) ++ memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN); ++ break; ++ case DEV_PATH_BRIDGE: ++ if (is_zero_ether_addr(info->h_source)) ++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); ++ ++ switch (path->bridge.vlan_mode) { ++ case DEV_PATH_BR_VLAN_UNTAG_HW: ++ info->ingress_vlans |= BIT(info->num_encaps - 1); ++ break; ++ case DEV_PATH_BR_VLAN_TAG: ++ info->encap[info->num_encaps].id = path->bridge.vlan_id; ++ info->encap[info->num_encaps].proto = path->bridge.vlan_proto; ++ info->num_encaps++; ++ break; ++ case DEV_PATH_BR_VLAN_UNTAG: ++ info->num_encaps--; ++ break; ++ case DEV_PATH_BR_VLAN_KEEP: ++ break; ++ } ++ break; ++ default: ++ info->indev = NULL; ++ break; ++ } ++ } ++ if (!info->outdev) ++ info->outdev = info->indev; ++ ++ info->hw_outdev = info->indev; ++ ++ if (nf_is_valid_ether_device(info->indev)) ++ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; ++} ++ ++static int nf_dev_fill_forward_path(const struct nf_flow_route *route, ++ const struct dst_entry *dst_cache, ++ const struct nf_conn *ct, ++ enum ip_conntrack_dir dir, u8 *ha, ++ struct net_device_path_stack *stack) ++{ ++ const void *daddr = &ct->tuplehash[!dir].tuple.src.u3; ++ struct net_device *dev = dst_cache->dev; ++ struct neighbour *n; ++ u8 nud_state; ++ ++ if (!nf_is_valid_ether_device(dev)) ++ goto out; ++ ++ n = dst_neigh_lookup(dst_cache, daddr); ++ if (!n) ++ return -1; ++ ++ read_lock_bh(&n->lock); ++ nud_state = n->nud_state; ++ ether_addr_copy(ha, n->ha); ++ read_unlock_bh(&n->lock); ++ neigh_release(n); ++ ++ if (!(nud_state & NUD_VALID)) ++ return -1; ++ ++out: ++ return dev_fill_forward_path(dev, ha, stack); ++} ++ ++static void nf_dev_forward_path(struct nf_flow_route *route, ++ const struct nf_conn *ct, ++ enum ip_conntrack_dir dir, ++ struct net_device **devs) ++{ ++ const struct dst_entry *dst = route->tuple[dir].dst; ++ struct net_device_path_stack stack; ++ struct nf_forward_info info = {}; ++ unsigned char ha[ETH_ALEN]; ++ int i; ++ ++ if (nf_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) ++ nf_dev_path_info(&stack, &info, ha); ++ ++ devs[!dir] = (struct net_device *)info.indev; ++ if (!info.indev) ++ return; ++ ++ route->tuple[!dir].in.ifindex = info.indev->ifindex; ++ for (i = 0; i < info.num_encaps; i++) { ++ route->tuple[!dir].in.encap[i].id = info.encap[i].id; ++ route->tuple[!dir].in.encap[i].proto = info.encap[i].proto; ++ } ++ route->tuple[!dir].in.num_encaps = info.num_encaps; ++ route->tuple[!dir].in.ingress_vlans = info.ingress_vlans; ++ ++ if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { ++ memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); ++ memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); ++ route->tuple[dir].out.ifindex = info.outdev->ifindex; ++ route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex; ++ route->tuple[dir].xmit_type = info.xmit_type; ++ } ++} ++ ++static int ++xt_flowoffload_route(struct sk_buff *skb, const struct nf_conn *ct, ++ const struct xt_action_param *par, ++ struct nf_flow_route *route, enum ip_conntrack_dir dir, ++ struct net_device **devs) ++{ ++ struct dst_entry *this_dst = skb_dst(skb); ++ struct dst_entry *other_dst = NULL; ++ struct flowi fl; ++ ++ memset(&fl, 0, sizeof(fl)); ++ switch (xt_family(par)) { ++ case NFPROTO_IPV4: ++ fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip; ++ fl.u.ip4.flowi4_oif = xt_in(par)->ifindex; ++ break; ++ case NFPROTO_IPV6: ++ fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.dst.u3.in6; ++ fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6; ++ fl.u.ip6.flowi6_oif = xt_in(par)->ifindex; ++ break; ++ } ++ ++ if (!dst_hold_safe(this_dst)) ++ return -ENOENT; ++ ++ nf_route(xt_net(par), &other_dst, &fl, false, xt_family(par)); ++ if (!other_dst) { ++ dst_release(this_dst); ++ return -ENOENT; ++ } ++ ++ nf_default_forward_path(route, this_dst, dir, devs); ++ nf_default_forward_path(route, other_dst, !dir, devs); ++ ++ if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH && ++ route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) { ++ nf_dev_forward_path(route, ct, dir, devs); ++ nf_dev_forward_path(route, ct, !dir, devs); ++ } ++ ++ return 0; ++} ++ ++static unsigned int ++flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par) ++{ ++ struct xt_flowoffload_table *table; ++ const struct xt_flowoffload_target_info *info = par->targinfo; ++ struct tcphdr _tcph, *tcph = NULL; ++ enum ip_conntrack_info ctinfo; ++ enum ip_conntrack_dir dir; ++ struct nf_flow_route route = {}; ++ struct flow_offload *flow = NULL; ++ struct net_device *devs[2] = {}; ++ struct nf_conn *ct; ++ struct net *net; ++ ++ if (xt_flowoffload_skip(skb, xt_family(par))) ++ return XT_CONTINUE; ++ ++ ct = nf_ct_get(skb, &ctinfo); ++ if (ct == NULL) ++ return XT_CONTINUE; ++ ++ switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) { ++ case IPPROTO_TCP: ++ if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) ++ return XT_CONTINUE; ++ ++ tcph = skb_header_pointer(skb, par->thoff, ++ sizeof(_tcph), &_tcph); ++ if (unlikely(!tcph || tcph->fin || tcph->rst)) ++ return XT_CONTINUE; ++ break; ++ case IPPROTO_UDP: ++ break; ++ default: ++ return XT_CONTINUE; ++ } ++ ++ if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) || ++ ct->status & (IPS_SEQ_ADJUST | IPS_NAT_CLASH)) ++ return XT_CONTINUE; ++ ++ if (!nf_ct_is_confirmed(ct)) ++ return XT_CONTINUE; ++ ++ dir = CTINFO2DIR(ctinfo); ++ ++ devs[dir] = xt_out(par); ++ devs[!dir] = xt_in(par); ++ ++ if (!devs[dir] || !devs[!dir]) ++ return XT_CONTINUE; ++ ++ if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status)) ++ return XT_CONTINUE; ++ ++ if (xt_flowoffload_route(skb, ct, par, &route, dir, devs) < 0) ++ goto err_flow_route; ++ ++ flow = flow_offload_alloc(ct); ++ if (!flow) ++ goto err_flow_alloc; ++ ++ flow_offload_route_init(flow, &route); ++ ++ if (tcph) { ++ ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; ++ ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; ++ } ++ ++ table = &flowtable[!!(info->flags & XT_FLOWOFFLOAD_HW)]; ++ ++ net = read_pnet(&table->ft.net); ++ if (!net) ++ write_pnet(&table->ft.net, xt_net(par)); ++ ++ __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags); ++ if (flow_offload_add(&table->ft, flow) < 0) ++ goto err_flow_add; ++ ++ xt_flowoffload_check_device(table, devs[0]); ++ xt_flowoffload_check_device(table, devs[1]); ++ ++ return XT_CONTINUE; ++ ++err_flow_add: ++ flow_offload_free(flow); ++err_flow_alloc: ++ dst_release(route.tuple[dir].dst); ++ dst_release(route.tuple[!dir].dst); ++err_flow_route: ++ clear_bit(IPS_OFFLOAD_BIT, &ct->status); ++ ++ return XT_CONTINUE; ++} ++ ++static int flowoffload_chk(const struct xt_tgchk_param *par) ++{ ++ struct xt_flowoffload_target_info *info = par->targinfo; ++ ++ if (info->flags & ~XT_FLOWOFFLOAD_MASK) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static struct xt_target offload_tg_reg __read_mostly = { ++ .family = NFPROTO_UNSPEC, ++ .name = "FLOWOFFLOAD", ++ .revision = 0, ++ .targetsize = sizeof(struct xt_flowoffload_target_info), ++ .usersize = sizeof(struct xt_flowoffload_target_info), ++ .checkentry = flowoffload_chk, ++ .target = flowoffload_tg, ++ .me = THIS_MODULE, ++}; ++ ++static int flow_offload_netdev_event(struct notifier_block *this, ++ unsigned long event, void *ptr) ++{ ++ struct xt_flowoffload_hook *hook0, *hook1; ++ struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ ++ if (event != NETDEV_UNREGISTER) ++ return NOTIFY_DONE; ++ ++ spin_lock_bh(&hooks_lock); ++ hook0 = flow_offload_lookup_hook(&flowtable[0], dev); ++ if (hook0) ++ hlist_del(&hook0->list); ++ ++ hook1 = flow_offload_lookup_hook(&flowtable[1], dev); ++ if (hook1) ++ hlist_del(&hook1->list); ++ spin_unlock_bh(&hooks_lock); ++ ++ if (hook0) { ++ nf_unregister_net_hook(hook0->net, &hook0->ops); ++ kfree(hook0); ++ } ++ ++ if (hook1) { ++ nf_unregister_net_hook(hook1->net, &hook1->ops); ++ kfree(hook1); ++ } ++ ++ nf_flow_table_cleanup(dev); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block flow_offload_netdev_notifier = { ++ .notifier_call = flow_offload_netdev_event, ++}; ++ ++static int nf_flow_rule_route_inet(struct net *net, ++ struct flow_offload *flow, ++ enum flow_offload_tuple_dir dir, ++ struct nf_flow_rule *flow_rule) ++{ ++ const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple; ++ int err; ++ ++ switch (flow_tuple->l3proto) { ++ case NFPROTO_IPV4: ++ err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule); ++ break; ++ case NFPROTO_IPV6: ++ err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule); ++ break; ++ default: ++ err = -1; ++ break; ++ } ++ ++ return err; ++} ++ ++static struct nf_flowtable_type flowtable_inet = { ++ .family = NFPROTO_INET, ++ .init = nf_flow_table_init, ++ .setup = nf_flow_table_offload_setup, ++ .action = nf_flow_rule_route_inet, ++ .free = nf_flow_table_free, ++ .hook = xt_flowoffload_net_hook, ++ .owner = THIS_MODULE, ++}; ++ ++static int init_flowtable(struct xt_flowoffload_table *tbl) ++{ ++ INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work); ++ tbl->ft.type = &flowtable_inet; ++ tbl->ft.flags = NF_FLOWTABLE_COUNTER; ++ ++ return nf_flow_table_init(&tbl->ft); ++} ++ ++static int __init xt_flowoffload_tg_init(void) ++{ ++ int ret; ++ ++ register_netdevice_notifier(&flow_offload_netdev_notifier); ++ ++ ret = init_flowtable(&flowtable[0]); ++ if (ret) ++ return ret; ++ ++ ret = init_flowtable(&flowtable[1]); ++ if (ret) ++ goto cleanup; ++ ++ flowtable[1].ft.flags |= NF_FLOWTABLE_HW_OFFLOAD; ++ ++ ret = xt_register_target(&offload_tg_reg); ++ if (ret) ++ goto cleanup2; ++ ++ return 0; ++ ++cleanup2: ++ nf_flow_table_free(&flowtable[1].ft); ++cleanup: ++ nf_flow_table_free(&flowtable[0].ft); ++ return ret; ++} ++ ++static void __exit xt_flowoffload_tg_exit(void) ++{ ++ xt_unregister_target(&offload_tg_reg); ++ unregister_netdevice_notifier(&flow_offload_netdev_notifier); ++ nf_flow_table_free(&flowtable[0].ft); ++ nf_flow_table_free(&flowtable[1].ft); ++} ++ ++MODULE_LICENSE("GPL"); ++module_init(xt_flowoffload_tg_init); ++module_exit(xt_flowoffload_tg_exit); +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -7,7 +7,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -373,8 +372,7 @@ flow_offload_lookup(struct nf_flowtable + } + EXPORT_SYMBOL_GPL(flow_offload_lookup); + +-static int +-nf_flow_table_iterate(struct nf_flowtable *flow_table, ++int nf_flow_table_iterate(struct nf_flowtable *flow_table, + void (*iter)(struct nf_flowtable *flowtable, + struct flow_offload *flow, void *data), + void *data) +@@ -435,6 +433,7 @@ static void nf_flow_offload_gc_step(stru + nf_flow_offload_stats(flow_table, flow); + } + } ++EXPORT_SYMBOL_GPL(nf_flow_table_iterate); + + void nf_flow_table_gc_run(struct nf_flowtable *flow_table) + { +--- /dev/null ++++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h +@@ -0,0 +1,17 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++#ifndef _XT_FLOWOFFLOAD_H ++#define _XT_FLOWOFFLOAD_H ++ ++#include ++ ++enum { ++ XT_FLOWOFFLOAD_HW = 1 << 0, ++ ++ XT_FLOWOFFLOAD_MASK = XT_FLOWOFFLOAD_HW ++}; ++ ++struct xt_flowoffload_target_info { ++ __u32 flags; ++}; ++ ++#endif /* _XT_FLOWOFFLOAD_H */ +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -294,6 +294,11 @@ void nf_flow_table_free(struct nf_flowta + + void flow_offload_teardown(struct flow_offload *flow); + ++int nf_flow_table_iterate(struct nf_flowtable *flow_table, ++ void (*iter)(struct nf_flowtable *flowtable, ++ struct flow_offload *flow, void *data), ++ void *data); ++ + void nf_flow_snat_port(const struct flow_offload *flow, + struct sk_buff *skb, unsigned int thoff, + u8 protocol, enum flow_offload_tuple_dir dir); diff --git a/target/linux/generic/hack-6.12/651-wireless_mesh_header.patch b/target/linux/generic/hack-6.12/651-wireless_mesh_header.patch new file mode 100644 index 00000000000..d3f85ea1d11 --- /dev/null +++ b/target/linux/generic/hack-6.12/651-wireless_mesh_header.patch @@ -0,0 +1,24 @@ +From 6d3bc769657b0ee7c7506dad9911111c4226a7ea Mon Sep 17 00:00:00 2001 +From: Imre Kaloz +Date: Fri, 7 Jul 2017 17:21:05 +0200 +Subject: mac80211: increase wireless mesh header size + +lede-commit 3d4466cfd8f75f717efdb1f96fdde3c70d865fc1 +Signed-off-by: Imre Kaloz +--- + include/linux/netdevice.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -159,8 +159,8 @@ static inline bool dev_xmit_complete(int + + #if defined(CONFIG_HYPERV_NET) + # define LL_MAX_HEADER 128 +-#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) +-# if defined(CONFIG_MAC80211_MESH) ++#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) || 1 ++# if defined(CONFIG_MAC80211_MESH) || 1 + # define LL_MAX_HEADER 128 + # else + # define LL_MAX_HEADER 96 diff --git a/target/linux/generic/hack-6.12/660-fq_codel_defaults.patch b/target/linux/generic/hack-6.12/660-fq_codel_defaults.patch new file mode 100644 index 00000000000..4d7b01d2437 --- /dev/null +++ b/target/linux/generic/hack-6.12/660-fq_codel_defaults.patch @@ -0,0 +1,27 @@ +From a6ccb238939b25851474a279b20367fd24a0e816 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:21:53 +0200 +Subject: hack: net: fq_codel: tune defaults for small devices + +Assume that x86_64 devices always have a big memory and do not need this +optimization compared to devices with only 32 MB or 64 MB RAM. + +Signed-off-by: Felix Fietkau +--- + net/sched/sch_fq_codel.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/sched/sch_fq_codel.c ++++ b/net/sched/sch_fq_codel.c +@@ -472,7 +472,11 @@ static int fq_codel_init(struct Qdisc *s + + sch->limit = 10*1024; + q->flows_cnt = 1024; ++#ifdef CONFIG_X86_64 + q->memory_limit = 32 << 20; /* 32 MBytes */ ++#else ++ q->memory_limit = 4 << 20; /* 4 MBytes */ ++#endif + q->drop_batch_size = 64; + q->quantum = psched_mtu(qdisc_dev(sch)); + INIT_LIST_HEAD(&q->new_flows); diff --git a/target/linux/generic/hack-6.12/661-kernel-ct-size-the-hashtable-more-adequately.patch b/target/linux/generic/hack-6.12/661-kernel-ct-size-the-hashtable-more-adequately.patch new file mode 100644 index 00000000000..b4bf160e824 --- /dev/null +++ b/target/linux/generic/hack-6.12/661-kernel-ct-size-the-hashtable-more-adequately.patch @@ -0,0 +1,25 @@ +From 804fbb3f2ec9283f7b778e057a68bfff440a0be6 Mon Sep 17 00:00:00 2001 +From: Rui Salvaterra +Date: Wed, 30 Mar 2022 22:51:55 +0100 +Subject: [PATCH] kernel: ct: size the hashtable more adequately + +To set the default size of the connection tracking hash table, a divider of +16384 becomes inadequate for a router handling lots of connections. Divide by +2048 instead, making the default size scale better with the available RAM. + +Signed-off-by: Rui Salvaterra +--- + net/netfilter/nf_conntrack_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -2648,7 +2648,7 @@ int nf_conntrack_init_start(void) + + if (!nf_conntrack_htable_size) { + nf_conntrack_htable_size +- = (((nr_pages << PAGE_SHIFT) / 16384) ++ = (((nr_pages << PAGE_SHIFT) / 2048) + / sizeof(struct hlist_head)); + if (BITS_PER_LONG >= 64 && + nr_pages > (4 * (1024 * 1024 * 1024 / PAGE_SIZE))) diff --git a/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch new file mode 100644 index 00000000000..ff235dd2529 --- /dev/null +++ b/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch @@ -0,0 +1,131 @@ +From 36e516290611e613aa92996cb4339561452695b4 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:24:23 +0200 +Subject: net: swconfig: adds openwrt switch layer + +Signed-off-by: Felix Fietkau +--- + drivers/net/phy/Kconfig | 83 +++++++++++++++++++++++++++++++++++++++++++++++ + drivers/net/phy/Makefile | 15 +++++++++ + include/uapi/linux/Kbuild | 1 + + 3 files changed, 99 insertions(+) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -77,6 +77,80 @@ config SFP + depends on HWMON || HWMON=n + select MDIO_I2C + ++comment "Switch configuration API + drivers" ++ ++config SWCONFIG ++ tristate "Switch configuration API" ++ help ++ Switch configuration API using netlink. This allows ++ you to configure the VLAN features of certain switches. ++ ++config SWCONFIG_LEDS ++ bool "Switch LED trigger support" ++ depends on (SWCONFIG && LEDS_TRIGGERS) ++ ++config ADM6996_PHY ++ tristate "Driver for ADM6996 switches" ++ select SWCONFIG ++ help ++ Currently supports the ADM6996FC and ADM6996M switches. ++ Support for FC is very limited. ++ ++config AR8216_PHY ++ tristate "Driver for Atheros AR8216/8327 switches" ++ select SWCONFIG ++ select ETHERNET_PACKET_MANGLE ++ ++config AR8216_PHY_LEDS ++ bool "Atheros AR8216 switch LED support" ++ depends on (AR8216_PHY && LEDS_CLASS) ++ ++source "drivers/net/phy/b53/Kconfig" ++ ++config IP17XX_PHY ++ tristate "Driver for IC+ IP17xx switches" ++ select SWCONFIG ++ ++config PSB6970_PHY ++ tristate "Lantiq XWAY Tantos (PSB6970) Ethernet switch" ++ select SWCONFIG ++ ++config RTL8306_PHY ++ tristate "Driver for Realtek RTL8306S switches" ++ select SWCONFIG ++ ++config RTL8366_SMI ++ tristate "Driver for the RTL8366 SMI interface" ++ depends on GPIOLIB ++ help ++ This module implements the SMI interface protocol which is used ++ by some RTL8366 ethernet switch devices via the generic GPIO API. ++ ++if RTL8366_SMI ++ ++config RTL8366_SMI_DEBUG_FS ++ bool "RTL8366 SMI interface debugfs support" ++ depends on DEBUG_FS ++ default n ++ ++config RTL8366S_PHY ++ tristate "Driver for the Realtek RTL8366S switch" ++ select SWCONFIG ++ ++config RTL8366RB_PHY ++ tristate "Driver for the Realtek RTL8366RB switch" ++ select SWCONFIG ++ ++config RTL8367_PHY ++ tristate "Driver for the Realtek RTL8367R/M switches" ++ select SWCONFIG ++ ++config RTL8367B_PHY ++ tristate "Driver for the Realtek RTL8367R-VB switch" ++ select SWCONFIG ++ ++endif # RTL8366_SMI ++ + comment "MII PHY device drivers" + + config AS21XXX_PHY +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -27,6 +27,21 @@ libphy-$(CONFIG_OPEN_ALLIANCE_HELPERS) + + obj-$(CONFIG_PHYLINK) += phylink.o + obj-$(CONFIG_PHYLIB) += libphy.o + ++obj-$(CONFIG_SWCONFIG) += swconfig.o ++obj-$(CONFIG_ADM6996_PHY) += adm6996.o ++obj-$(CONFIG_AR8216_PHY) += ar8xxx.o ++ar8xxx-y += ar8216.o ++ar8xxx-y += ar8327.o ++obj-$(CONFIG_SWCONFIG_B53) += b53/ ++obj-$(CONFIG_IP17XX_PHY) += ip17xx.o ++obj-$(CONFIG_PSB6970_PHY) += psb6970.o ++obj-$(CONFIG_RTL8306_PHY) += rtl8306.o ++obj-$(CONFIG_RTL8366_SMI) += rtl8366_smi.o ++obj-$(CONFIG_RTL8366S_PHY) += rtl8366s.o ++obj-$(CONFIG_RTL8366RB_PHY) += rtl8366rb.o ++obj-$(CONFIG_RTL8367_PHY) += rtl8367.o ++obj-$(CONFIG_RTL8367B_PHY) += rtl8367b.o ++ + obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o + + obj-$(CONFIG_SFP) += sfp.o +--- a/include/linux/platform_data/b53.h ++++ b/include/linux/platform_data/b53.h +@@ -29,6 +29,9 @@ struct b53_platform_data { + u32 chip_id; + u16 enabled_ports; + ++ /* allow to specify an ethX alias */ ++ const char *alias; ++ + /* only used by MMAP'd driver */ + unsigned big_endian:1; + void __iomem *regs; diff --git a/target/linux/generic/hack-6.12/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/target/linux/generic/hack-6.12/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch new file mode 100644 index 00000000000..29ac3e9734f --- /dev/null +++ b/target/linux/generic/hack-6.12/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch @@ -0,0 +1,21 @@ +From ebd924d773223593142d417c41d4ee6fa16f1805 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:45:56 +0200 +Subject: [PATCH] net/dsa/mv88e6xxx: disable ATU violation + +--- + drivers/net/dsa/mv88e6xxx/chip.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -3583,6 +3583,9 @@ static int mv88e6xxx_setup_port(struct m + else + reg = 1 << port; + ++ /* Disable ATU member violation interrupt */ ++ reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG; ++ + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, + reg); + if (err) diff --git a/target/linux/generic/hack-6.12/721-net-add-packet-mangeling.patch b/target/linux/generic/hack-6.12/721-net-add-packet-mangeling.patch new file mode 100644 index 00000000000..32228bb34c1 --- /dev/null +++ b/target/linux/generic/hack-6.12/721-net-add-packet-mangeling.patch @@ -0,0 +1,159 @@ +From ffe387740bbe88dd88bbe04d6375902708003d6e Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:25:00 +0200 +Subject: net: add packet mangeling + +ar8216 switches have a hardware bug, which renders normal 802.1q support +unusable. Packet mangling is required to fix up the vlan for incoming +packets. + +Signed-off-by: Felix Fietkau +--- + include/linux/netdevice.h | 10 ++++++++++ + include/linux/skbuff.h | 14 ++++---------- + net/Kconfig | 6 ++++++ + net/core/dev.c | 20 +++++++++++++++----- + net/core/skbuff.c | 17 +++++++++++++++++ + net/ethernet/eth.c | 6 ++++++ + 6 files changed, 58 insertions(+), 15 deletions(-) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -1687,6 +1687,7 @@ enum netdev_priv_flags { + IFF_L3MDEV_RX_HANDLER = 1<<29, + IFF_NO_ADDRCONF = BIT_ULL(30), + IFF_TX_SKB_NO_LINEAR = BIT_ULL(31), ++ IFF_NO_IP_ALIGN = BIT_ULL(32), + }; + + /* Specifies the type of the struct net_device::ml_priv pointer */ +@@ -2168,6 +2169,11 @@ struct net_device { + const struct tlsdev_ops *tlsdev_ops; + #endif + ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ void (*eth_mangle_rx)(struct net_device *dev, struct sk_buff *skb); ++ struct sk_buff *(*eth_mangle_tx)(struct net_device *dev, struct sk_buff *skb); ++#endif ++ + unsigned int operstate; + unsigned char link_mode; + +@@ -2237,6 +2243,10 @@ struct net_device { + struct mctp_dev __rcu *mctp_ptr; + #endif + ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ void *phy_ptr; /* PHY device specific data */ ++#endif ++ + /* + * Cache lines mostly used on receive path (including eth_type_trans()) + */ +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -3274,6 +3274,10 @@ static inline int pskb_trim(struct sk_bu + return (len < skb->len) ? __pskb_trim(skb, len) : 0; + } + ++extern struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, ++ unsigned int length, gfp_t gfp); ++ ++ + /** + * pskb_trim_unique - remove end from a paged unique (not cloned) buffer + * @skb: buffer to alter +@@ -3439,16 +3443,6 @@ static inline struct sk_buff *dev_alloc_ + } + + +-static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, +- unsigned int length, gfp_t gfp) +-{ +- struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); +- +- if (NET_IP_ALIGN && skb) +- skb_reserve(skb, NET_IP_ALIGN); +- return skb; +-} +- + static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, + unsigned int length) + { +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -26,6 +26,12 @@ menuconfig NET + + if NET + ++config ETHERNET_PACKET_MANGLE ++ bool ++ help ++ This option can be selected by phy drivers that need to mangle ++ packets going in or out of an ethernet device. ++ + config WANT_COMPAT_NETLINK_MESSAGES + bool + help +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -3671,6 +3671,11 @@ static int xmit_one(struct sk_buff *skb, + if (dev_nit_active(dev)) + dev_queue_xmit_nit(skb, dev); + ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ if (dev->eth_mangle_tx && !(skb = dev->eth_mangle_tx(dev, skb))) ++ return NETDEV_TX_OK; ++#endif ++ + len = skb->len; + trace_net_dev_start_xmit(skb, dev); + rc = netdev_start_xmit(skb, dev, txq, more); +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -64,6 +64,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -326,6 +327,22 @@ void *__napi_alloc_frag_align(unsigned i + } + EXPORT_SYMBOL(__napi_alloc_frag_align); + ++struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, ++ unsigned int length, gfp_t gfp) ++{ ++ struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); ++ ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ if (dev && (dev->priv_flags & IFF_NO_IP_ALIGN)) ++ return skb; ++#endif ++ ++ if (NET_IP_ALIGN && skb) ++ skb_reserve(skb, NET_IP_ALIGN); ++ return skb; ++} ++EXPORT_SYMBOL(__netdev_alloc_skb_ip_align); ++ + void *__netdev_alloc_frag_align(unsigned int fragsz, unsigned int align_mask) + { + void *data; +--- a/net/ethernet/eth.c ++++ b/net/ethernet/eth.c +@@ -159,6 +159,12 @@ __be16 eth_type_trans(struct sk_buff *sk + const struct ethhdr *eth; + + skb->dev = dev; ++ ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ if (dev->eth_mangle_rx) ++ dev->eth_mangle_rx(dev, skb); ++#endif ++ + skb_reset_mac_header(skb); + + eth = eth_skb_pull_mac(skb); diff --git a/target/linux/generic/hack-6.12/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch b/target/linux/generic/hack-6.12/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch new file mode 100644 index 00000000000..2d7edc9e752 --- /dev/null +++ b/target/linux/generic/hack-6.12/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch @@ -0,0 +1,117 @@ +From 5f62951fba63a9f9cfff564209426bdea5fcc371 Mon Sep 17 00:00:00 2001 +From: Alex Marginean +Date: Tue, 27 Aug 2019 15:16:56 +0300 +Subject: [PATCH] drivers: net: phy: aquantia: enable AQR112 and AQR412 + +Adds support for AQR112 and AQR412 which is mostly based on existing code +with the addition of code configuring the protocol on system side. +This allows changing the system side protocol without having to deploy a +different firmware on the PHY. + +Signed-off-by: Alex Marginean +--- + drivers/net/phy/aquantia/aquantia_main.c | 88 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 88 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -97,6 +97,29 @@ + #define AQR107_OP_IN_PROG_SLEEP 1000 + #define AQR107_OP_IN_PROG_TIMEOUT 100000 + ++/* registers in MDIO_MMD_VEND1 region */ ++#define AQUANTIA_VND1_GLOBAL_SC 0x000 ++#define AQUANTIA_VND1_GLOBAL_SC_LP BIT(0xb) ++ ++/* global start rate, the protocol associated with this speed is used by default ++ * on SI. ++ */ ++#define AQUANTIA_VND1_GSTART_RATE 0x31a ++#define AQUANTIA_VND1_GSTART_RATE_OFF 0 ++#define AQUANTIA_VND1_GSTART_RATE_100M 1 ++#define AQUANTIA_VND1_GSTART_RATE_1G 2 ++#define AQUANTIA_VND1_GSTART_RATE_10G 3 ++#define AQUANTIA_VND1_GSTART_RATE_2_5G 4 ++#define AQUANTIA_VND1_GSTART_RATE_5G 5 ++ ++/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */ ++#define AQUANTIA_VND1_GSYSCFG_BASE 0x31b ++#define AQUANTIA_VND1_GSYSCFG_100M 0 ++#define AQUANTIA_VND1_GSYSCFG_1G 1 ++#define AQUANTIA_VND1_GSYSCFG_2_5G 2 ++#define AQUANTIA_VND1_GSYSCFG_5G 3 ++#define AQUANTIA_VND1_GSYSCFG_10G 4 ++ + static int aqr107_get_sset_count(struct phy_device *phydev) + { + return AQR107_SGMII_STAT_SZ; +@@ -203,6 +226,51 @@ static int aqr_config_aneg(struct phy_de + return genphy_c45_check_and_restart_aneg(phydev, changed); + } + ++static struct { ++ u16 syscfg; ++ int cnt; ++ u16 start_rate; ++} aquantia_syscfg[PHY_INTERFACE_MODE_MAX] = { ++ [PHY_INTERFACE_MODE_SGMII] = {0x04b, AQUANTIA_VND1_GSYSCFG_1G, ++ AQUANTIA_VND1_GSTART_RATE_1G}, ++ [PHY_INTERFACE_MODE_2500BASEX] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G, ++ AQUANTIA_VND1_GSTART_RATE_2_5G}, ++ [PHY_INTERFACE_MODE_XGMII] = {0x100, AQUANTIA_VND1_GSYSCFG_10G, ++ AQUANTIA_VND1_GSTART_RATE_10G}, ++ [PHY_INTERFACE_MODE_USXGMII] = {0x080, AQUANTIA_VND1_GSYSCFG_10G, ++ AQUANTIA_VND1_GSTART_RATE_10G}, ++}; ++ ++/* Sets up protocol on system side before calling aqr_config_aneg */ ++static int aqr_config_aneg_set_prot(struct phy_device *phydev) ++{ ++ int if_type = phydev->interface; ++ int i; ++ ++ if (!aquantia_syscfg[if_type].cnt) ++ return 0; ++ ++ /* set PHY in low power mode so we can configure protocols */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, ++ AQUANTIA_VND1_GLOBAL_SC_LP); ++ mdelay(10); ++ ++ /* set the default rate to enable the SI link */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE, ++ aquantia_syscfg[if_type].start_rate); ++ ++ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ AQUANTIA_VND1_GSYSCFG_BASE + i, ++ aquantia_syscfg[if_type].syscfg); ++ ++ /* wake PHY back up */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0); ++ mdelay(10); ++ ++ return aqr_config_aneg(phydev); ++} ++ + static int aqr_config_intr(struct phy_device *phydev) + { + bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; +@@ -972,7 +1040,7 @@ static struct phy_driver aqr_driver[] = + PHY_ID_MATCH_MODEL(PHY_ID_AQR112), + .name = "Aquantia AQR112", + .probe = aqr107_probe, +- .config_aneg = aqr_config_aneg, ++ .config_aneg = aqr_config_aneg_set_prot, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .get_tunable = aqr107_get_tunable, +@@ -995,7 +1063,7 @@ static struct phy_driver aqr_driver[] = + PHY_ID_MATCH_MODEL(PHY_ID_AQR412), + .name = "Aquantia AQR412", + .probe = aqr107_probe, +- .config_aneg = aqr_config_aneg, ++ .config_aneg = aqr_config_aneg_set_prot, + .config_intr = aqr_config_intr, + .handle_interrupt = aqr_handle_interrupt, + .get_tunable = aqr107_get_tunable, diff --git a/target/linux/generic/hack-6.12/723-net-phy-aquantia-fix-system-side-protocol-mi.patch b/target/linux/generic/hack-6.12/723-net-phy-aquantia-fix-system-side-protocol-mi.patch new file mode 100644 index 00000000000..eaeb3b6a926 --- /dev/null +++ b/target/linux/generic/hack-6.12/723-net-phy-aquantia-fix-system-side-protocol-mi.patch @@ -0,0 +1,34 @@ +From 5f008cb22f60da4e10375f22266c1a4e20b1252e Mon Sep 17 00:00:00 2001 +From: Alex Marginean +Date: Fri, 20 Sep 2019 18:22:52 +0300 +Subject: [PATCH] drivers: net: phy: aquantia: fix system side protocol + misconfiguration + +Do not set up protocols for speeds that are not supported by FW. Enabling +these protocols leads to link issues on system side. + +Signed-off-by: Alex Marginean +--- + drivers/net/phy/aquantia/aquantia_main.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -259,10 +259,16 @@ static int aqr_config_aneg_set_prot(stru + phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE, + aquantia_syscfg[if_type].start_rate); + +- for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) ++ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) { ++ u16 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ AQUANTIA_VND1_GSYSCFG_BASE + i); ++ if (!reg) ++ continue; ++ + phy_write_mmd(phydev, MDIO_MMD_VEND1, + AQUANTIA_VND1_GSYSCFG_BASE + i, + aquantia_syscfg[if_type].syscfg); ++ } + + /* wake PHY back up */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0); diff --git a/target/linux/generic/hack-6.12/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch b/target/linux/generic/hack-6.12/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch new file mode 100644 index 00000000000..98c4186a3ed --- /dev/null +++ b/target/linux/generic/hack-6.12/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch @@ -0,0 +1,63 @@ +From 3b92ee7b7899b6beffb2b484c58326e36612a873 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 23 Dec 2021 14:52:56 +0000 +Subject: [PATCH] net: phy: aquantia: add PHY_ID for AQR112R + +As advised by Ian Chang this PHY is used in Puzzle devices. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/aquantia/aquantia_main.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -32,6 +32,8 @@ + #define PHY_ID_AQR114C 0x31c31c22 + #define PHY_ID_AQR115C 0x31c31c33 + #define PHY_ID_AQR813 0x31c31cb2 ++#define PHY_ID_AQR112C 0x03a1b790 ++#define PHY_ID_AQR112R 0x31c31d12 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 + #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) +@@ -1205,6 +1207,30 @@ static struct phy_driver aqr_driver[] = + .led_hw_control_get = aqr_phy_led_hw_control_get, + .led_polarity_set = aqr_phy_led_polarity_set, + }, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112C), ++ .name = "Aquantia AQR112C", ++ .probe = aqr107_probe, ++ .config_aneg = aqr_config_aneg_set_prot, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112R), ++ .name = "Aquantia AQR112R", ++ .probe = aqr107_probe, ++ .config_aneg = aqr_config_aneg_set_prot, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++}, + }; + + module_phy_driver(aqr_driver); +@@ -1226,6 +1252,8 @@ static const struct mdio_device_id __may + { PHY_ID_MATCH_MODEL(PHY_ID_AQR114C) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR115C) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112C) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112R) }, + { } + }; + diff --git a/target/linux/generic/hack-6.12/730-net-ethernet-mtk_eth_soc-add-hw-dump-for-forced-rese.patch b/target/linux/generic/hack-6.12/730-net-ethernet-mtk_eth_soc-add-hw-dump-for-forced-rese.patch new file mode 100644 index 00000000000..39446f6bace --- /dev/null +++ b/target/linux/generic/hack-6.12/730-net-ethernet-mtk_eth_soc-add-hw-dump-for-forced-rese.patch @@ -0,0 +1,115 @@ +From bc51c337a3147c4a02c743489885a6657bc5371c Mon Sep 17 00:00:00 2001 +From: Bo-Cun Chen +Date: Wed, 27 Nov 2024 13:36:49 +0800 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add hw dump for forced reset + +Without this patch, the ETH driver is unable to dump the registers +before triggering a forced reset. + +Signed-off-by: Bo-Cun Chen +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 55 +++++++++++++++++++++ + 1 files changed, 55 insertions(+) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -74,6 +74,7 @@ static const struct mtk_reg_map mtk_reg_ + .rx_ptr = 0x1900, + .rx_cnt_cfg = 0x1904, + .qcrx_ptr = 0x1908, ++ .page = 0x19f0, + .glo_cfg = 0x1a04, + .rst_idx = 0x1a08, + .delay_irq = 0x1a0c, +@@ -140,6 +141,7 @@ static const struct mtk_reg_map mt7986_r + .rx_ptr = 0x4500, + .rx_cnt_cfg = 0x4504, + .qcrx_ptr = 0x4508, ++ .page = 0x45f0, + .glo_cfg = 0x4604, + .rst_idx = 0x4608, + .delay_irq = 0x460c, +@@ -191,6 +193,7 @@ static const struct mtk_reg_map mt7988_r + .rx_ptr = 0x4500, + .rx_cnt_cfg = 0x4504, + .qcrx_ptr = 0x4508, ++ .page = 0x45f0, + .glo_cfg = 0x4604, + .rst_idx = 0x4608, + .delay_irq = 0x460c, +@@ -4069,6 +4072,56 @@ static void mtk_set_mcr_max_rx(struct mt + mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); + } + ++static void mtk_hw_dump_reg(struct mtk_eth *eth, char *name, u32 offset, u32 range) ++{ ++ u32 cur = offset; ++ ++ pr_info("\n==================== %s ====================\n", name); ++ while (cur < offset + range) { ++ pr_info("0x%08x: %08x %08x %08x %08x\n", ++ cur, mtk_r32(eth, cur), mtk_r32(eth, cur + 0x4), ++ mtk_r32(eth, cur + 0x8), mtk_r32(eth, cur + 0xc)); ++ cur += 0x10; ++ } ++} ++ ++static void mtk_hw_dump(struct mtk_eth *eth) ++{ ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; ++ u32 id; ++ ++ mtk_hw_dump_reg(eth, "FE", 0x0, 0x600); ++ mtk_hw_dump_reg(eth, "FE", 0x1400, 0x300); ++ mtk_hw_dump_reg(eth, "ADMA", reg_map->pdma.rx_ptr, 0x300); ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { ++ for (id = 0; id < MTK_QDMA_NUM_QUEUES / 16; id++) { ++ mtk_w32(eth, id, reg_map->qdma.page); ++ pr_info("\nQDMA PAGE:%x ", mtk_r32(eth, reg_map->qdma.page)); ++ mtk_hw_dump_reg(eth, "QDMA", reg_map->qdma.qtx_cfg, 0x100); ++ mtk_w32(eth, 0, reg_map->qdma.page); ++ } ++ mtk_hw_dump_reg(eth, "QDMA", reg_map->qdma.rx_ptr, 0x300); ++ } ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { ++ mtk_hw_dump_reg(eth, "WDMA0", reg_map->wdma_base[0], 0x400); ++ mtk_hw_dump_reg(eth, "WDMA1", reg_map->wdma_base[1], 0x400); ++ if (mtk_is_netsys_v3_or_greater(eth)) ++ mtk_hw_dump_reg(eth, "WDMA2", reg_map->wdma_base[2], 0x400); ++ } ++ mtk_hw_dump_reg(eth, "PPE0", reg_map->ppe_base + 0x200, 0x200); ++ if (!mtk_is_netsys_v1(eth)) ++ mtk_hw_dump_reg(eth, "PPE1", reg_map->ppe_base + 0x600, 0x200); ++ if (mtk_is_netsys_v3_or_greater(eth)) ++ mtk_hw_dump_reg(eth, "PPE2", reg_map->ppe_base + 0xE00, 0x200); ++ mtk_hw_dump_reg(eth, "GMAC", 0x10000, 0x300); ++ if (mtk_is_netsys_v3_or_greater(eth)) ++ mtk_hw_dump_reg(eth, "GMAC", 0x10300, 0x100); ++ if (mtk_is_netsys_v3_or_greater(eth)) { ++ mtk_hw_dump_reg(eth, "XGMAC0", 0x12000, 0x300); ++ mtk_hw_dump_reg(eth, "XGMAC1", 0x13000, 0x300); ++ } ++} ++ + static void mtk_hw_reset(struct mtk_eth *eth) + { + u32 val; +@@ -4548,6 +4601,8 @@ static void mtk_pending_work(struct work + rtnl_lock(); + set_bit(MTK_RESETTING, ð->state); + ++ mtk_hw_dump(eth); ++ + mtk_prepare_for_reset(eth); + mtk_wed_fe_reset(); + /* Run again reset preliminary configuration in order to avoid any +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1185,6 +1185,7 @@ struct mtk_reg_map { + u32 rx_ptr; /* rx base pointer */ + u32 rx_cnt_cfg; /* rx max count configuration */ + u32 qcrx_ptr; /* rx cpu pointer */ ++ u32 page; /* page configuration */ + u32 glo_cfg; /* global configuration */ + u32 rst_idx; /* reset index */ + u32 delay_irq; /* delay interrupt */ diff --git a/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch b/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch new file mode 100644 index 00000000000..c9dc2508ac5 --- /dev/null +++ b/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch @@ -0,0 +1,21 @@ +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -431,6 +431,8 @@ config QSEMI_PHY + + source "drivers/net/phy/realtek/Kconfig" + ++source "drivers/net/phy/rtl8261n/Kconfig" ++ + config RENESAS_PHY + tristate "Renesas PHYs" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -110,6 +110,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja + obj-y += qcom/ + obj-$(CONFIG_QSEMI_PHY) += qsemi.o + obj-$(CONFIG_REALTEK_PHY) += realtek/ ++obj-y += rtl8261n/ + obj-$(CONFIG_RENESAS_PHY) += uPD60620.o + obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o + obj-$(CONFIG_SMSC_PHY) += smsc.o diff --git a/target/linux/generic/hack-6.12/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch b/target/linux/generic/hack-6.12/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch new file mode 100644 index 00000000000..bf26a2f6ec1 --- /dev/null +++ b/target/linux/generic/hack-6.12/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch @@ -0,0 +1,64 @@ +From 880d1311335120f64447ca9d11933872d734e19a Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 27 Mar 2023 18:41:54 +0100 +Subject: [PATCH] generic: pcs-mtk-lynxi: add hack to use 2500Base-X without AN + +Using 2500Base-T SFP modules e.g. on the BananaPi R3 requires manually +disabling auto-negotiation, e.g. using ethtool. While a proper fix +using SFP quirks is being discussed upstream, bring a work-around to +restore user experience to what it was before the switch to the +dedicated SGMII PCS driver. + +Signed-off-by: Daniel Golle + +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -129,14 +129,23 @@ static void mtk_pcs_lynxi_get_state(stru + struct phylink_link_state *state) + { + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); +- unsigned int bm, adv; ++ unsigned int bm, bmsr, adv; + + /* Read the BMSR and LPA */ + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); +- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ bmsr = FIELD_GET(SGMII_BMSR, bm); ++ ++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { ++ state->link = !!(bmsr & BMSR_LSTATUS); ++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); ++ state->speed = SPEED_2500; ++ state->duplex = DUPLEX_FULL; ++ ++ return; ++ } + +- phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm), +- FIELD_GET(SGMII_LPA, adv)); ++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ phylink_mii_c22_pcs_decode_state(state, bmsr, FIELD_GET(SGMII_LPA, adv)); + } + + static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs) +@@ -157,7 +166,7 @@ static int mtk_pcs_lynxi_config(struct p + { + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + bool mode_changed = false, changed; +- unsigned int rgc3, sgm_mode, bmcr; ++ unsigned int rgc3, sgm_mode, bmcr = 0; + int advertise, link_timer; + + advertise = phylink_mii_c22_pcs_encode_advertisement(interface, +@@ -180,9 +189,8 @@ static int mtk_pcs_lynxi_config(struct p + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { + if (interface == PHY_INTERFACE_MODE_SGMII) + sgm_mode |= SGMII_SPEED_DUPLEX_AN; +- bmcr = BMCR_ANENABLE; +- } else { +- bmcr = 0; ++ if (interface != PHY_INTERFACE_MODE_2500BASEX) ++ bmcr = BMCR_ANENABLE; + } + + if (mpcs->interface != interface) { diff --git a/target/linux/generic/hack-6.12/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch b/target/linux/generic/hack-6.12/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch new file mode 100644 index 00000000000..28c4c702b37 --- /dev/null +++ b/target/linux/generic/hack-6.12/755-net-phy-motorcomm-yt8821-bus-collision-workaround.patch @@ -0,0 +1,85 @@ +From 63161fb6353493c648a260244f6ac7eca65fd48e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jakub=20Van=C4=9Bk?= +Date: Mon, 2 Mar 2026 20:31:37 +0100 +Subject: [PATCH] net: phy: Work around MDIO collisions in the YT8821 driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The Cudy M3000 router suffers from a MDIO bus collision: +- The MT7981B internal gigabit PHY listens on address 0. +- The Motorcomm YT8821 is strapped to listen on address 1, + but it initially also listens on address 0 (Motorcomm + incorrectly considers that address a broadcast address). + +Without this patch, the YT8821 reacts to MDIO commands intended +for the internal gigabit PHY and that makes it work unreliably. +The YT8821 state somehow gets corrupted by the commands for the MT7981 +(e.g. we get 100Mbps speeds on gigabit links). + +This dirty workaround resets the PHY to cancel out any earlier +YT8821 register corruption and then it disables the address 0 +as soon as possible. This should make the YT8821 work reliably. + +BEWARE though: the PHY will be reset only if the device tree +node that defines the PHY carries the reset-gpios attribute. +The PHY will not be reset if the reset GPIO is either on the MDIO +bus node or is not defined at all. This might not always be a problem -- +some routers do not require the PHY to be reset (e.g. Cudy WR3000H, +where there likely is less communication with the MT7981B PHY, +and so the YT8821 PHY works reasonably well there even without this patch). + +This patch also does not address the possible MDIO bus collisions +when the MT7981B PHY driver initially configures that PHY and invokes +some read transactions on the bus. I am hoping that the MT7981B MDIO +bus controller gives priority to the internal PHY and so it would +not be affected by these collisions. However, I don't have anything +to base this on apart from it "seeming to be working okay". + +This patch is unlikely to be mergeable into the mainline kernel. +Upstream has strongly indicated that they would prefer this problem +to be resolved by the platform bootloader (e.g. U-Boot), see +- https://lore.kernel.org/all/d3bb9c36-5a0e-4339-901d-2dd21bdba395@gmail.com/ +- https://lore.kernel.org/all/20260228232241.1274236-1-linuxtardis@gmail.com/ + +Signed-off-by: Jakub Vaněk +--- + drivers/net/phy/motorcomm.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/drivers/net/phy/motorcomm.c ++++ b/drivers/net/phy/motorcomm.c +@@ -214,6 +214,9 @@ + #define YT8521_RC1R_RGMII_2_100_NS 14 + #define YT8521_RC1R_RGMII_2_250_NS 15 + ++#define YTPHY_MDIO_ADDRESS_CONTROL_REG 0xA005 ++#define YTPHY_MACR_EN_PHY_ADDR_0 BIT(6) ++ + #define YTPHY_MISC_CONFIG_REG 0xA006 + #define YTPHY_MCR_FIBER_SPEED_MASK BIT(0) + #define YTPHY_MCR_FIBER_1000BX (0x1 << 0) +@@ -2659,6 +2662,23 @@ static int yt8821_config_init(struct phy + int ret; + u16 set; + ++ /* Hard-reset the PHY to clear out any register corruption ++ * from preceding MDIO bus conflicts. ++ */ ++ phy_device_reset(phydev, 1); ++ ++ /* Deassert the reset GPIO under the MDIO bus lock to make ++ * sure that nothing will communicate on the bus until we ++ * disable the broadcast address in the YT8821. ++ */ ++ phy_lock_mdio_bus(phydev); ++ phy_device_reset(phydev, 0); ++ ret = ytphy_modify_ext(phydev, ++ YTPHY_MDIO_ADDRESS_CONTROL_REG, ++ YTPHY_MACR_EN_PHY_ADDR_0, ++ 0); ++ phy_unlock_mdio_bus(phydev); ++ + if (phydev->interface == PHY_INTERFACE_MODE_2500BASEX) + mode = YT8821_CHIP_MODE_FORCE_BX2500; + diff --git a/target/linux/generic/hack-6.12/760-net-usb-r8152-add-LED-configuration-from-OF.patch b/target/linux/generic/hack-6.12/760-net-usb-r8152-add-LED-configuration-from-OF.patch new file mode 100644 index 00000000000..14024e07cdd --- /dev/null +++ b/target/linux/generic/hack-6.12/760-net-usb-r8152-add-LED-configuration-from-OF.patch @@ -0,0 +1,74 @@ +From 82985725e071f2a5735052f18e109a32aeac3a0b Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Sun, 26 Jul 2020 02:38:31 +0200 +Subject: [PATCH] net: usb: r8152: add LED configuration from OF + +This adds the ability to configure the LED configuration register using +OF. This way, the correct value for board specific LED configuration can +be determined. + +Signed-off-by: David Bauer +--- + drivers/net/usb/r8152.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -7049,6 +7050,22 @@ static void rtl_tally_reset(struct r8152 + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data); + } + ++static int r8152_led_configuration(struct r8152 *tp) ++{ ++ u32 led_data; ++ int ret; ++ ++ ret = of_property_read_u32(tp->udev->dev.of_node, "realtek,led-data", ++ &led_data); ++ ++ if (ret) ++ return ret; ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LEDSEL, led_data); ++ ++ return 0; ++} ++ + static void r8152b_init(struct r8152 *tp) + { + u32 ocp_data; +@@ -7090,6 +7107,8 @@ static void r8152b_init(struct r8152 *tp + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); + ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); + ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); ++ ++ r8152_led_configuration(tp); + } + + static void r8153_init(struct r8152 *tp) +@@ -7230,6 +7249,8 @@ static void r8153_init(struct r8152 *tp) + tp->coalesce = COALESCE_SLOW; + break; + } ++ ++ r8152_led_configuration(tp); + } + + static void r8153b_init(struct r8152 *tp) +@@ -7312,6 +7333,8 @@ static void r8153b_init(struct r8152 *tp + rtl_tally_reset(tp); + + tp->coalesce = 15000; /* 15 us */ ++ ++ r8152_led_configuration(tp); + } + + static void r8153c_init(struct r8152 *tp) diff --git a/target/linux/generic/hack-6.12/761-dt-bindings-net-add-RTL8152-binding-documentation.patch b/target/linux/generic/hack-6.12/761-dt-bindings-net-add-RTL8152-binding-documentation.patch new file mode 100644 index 00000000000..be262b993cd --- /dev/null +++ b/target/linux/generic/hack-6.12/761-dt-bindings-net-add-RTL8152-binding-documentation.patch @@ -0,0 +1,54 @@ +From 3ee05f4aa64fc86af3be5bc176ba5808de9260a7 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Sun, 26 Jul 2020 15:30:33 +0200 +Subject: [PATCH] dt-bindings: net: add RTL8152 binding documentation + +Add binding documentation for the Realtek RTL8152 / RTL8153 USB ethernet +adapters. + +Signed-off-by: David Bauer +--- + .../bindings/net/realtek,rtl8152.yaml | 36 +++++++++++++++++++ + 1 file changed, 36 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/realtek,rtl8152.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/realtek,rtl8152.yaml +@@ -0,0 +1,36 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/net/realtek,rtl8152.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Realtek RTL8152/RTL8153 series USB ethernet ++ ++maintainers: ++ - David Bauer ++ ++properties: ++ compatible: ++ oneOf: ++ - items: ++ - enum: ++ - realtek,rtl8152 ++ - realtek,rtl8153 ++ ++ reg: ++ description: The device number on the USB bus ++ ++ realtek,led-data: ++ description: Value to be written to the LED configuration register. ++ ++required: ++ - compatible ++ - reg ++ ++examples: ++ - | ++ usb-eth@2 { ++ compatible = "realtek,rtl8153"; ++ reg = <2>; ++ realtek,led-data = <0x87>; ++ }; +\ No newline at end of file diff --git a/target/linux/generic/hack-6.12/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch b/target/linux/generic/hack-6.12/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch new file mode 100644 index 00000000000..2eeca8fd774 --- /dev/null +++ b/target/linux/generic/hack-6.12/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch @@ -0,0 +1,72 @@ +From cc225d163b5a4f7a0d1968298bf7927306646a47 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Fri, 28 Apr 2023 01:53:01 +0200 +Subject: [PATCH] net: phy: mediatek-ge: add LED configuration interface + +This adds a small hack similar to the one used for ar8xxx switches to +read a reg:value map for configuring the LED configuration registers. + +This allows OpenWrt to write device-specific LED action as well as blink +configurations. It is unlikely to be accepted upstream, as upstream +plans on integrating their own framework for handling these LEDs. + +Signed-off-by: David Bauer +--- + drivers/net/phy/mediatek/mtk-ge.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0+ ++#include + #include + #include + #include +@@ -73,6 +74,36 @@ static int mt7530_phy_config_init(struct + return 0; + } + ++static int mt7530_led_config_of(struct phy_device *phydev) ++{ ++ struct device_node *np = phydev->mdio.dev.of_node; ++ const __be32 *paddr; ++ int len; ++ int i; ++ ++ paddr = of_get_property(np, "mediatek,led-config", &len); ++ if (!paddr) ++ return 0; ++ ++ if (len < (2 * sizeof(*paddr))) ++ return -EINVAL; ++ ++ len /= sizeof(*paddr); ++ ++ phydev_warn(phydev, "Configure LED registers (num=%d)\n", len); ++ for (i = 0; i < len - 1; i += 2) { ++ u32 reg; ++ u32 val; ++ ++ reg = be32_to_cpup(paddr + i); ++ val = be32_to_cpup(paddr + i + 1); ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, reg, val); ++ } ++ ++ return 0; ++} ++ + static int mt7531_phy_config_init(struct phy_device *phydev) + { + mtk_gephy_config_init(phydev); +@@ -93,6 +124,9 @@ static int mt7531_phy_config_init(struct + FIELD_PREP(MTK_TX_DELAY_PAIR_B_MASK, 0x4) | + FIELD_PREP(MTK_TX_DELAY_PAIR_D_MASK, 0x4)); + ++ /* LED Config*/ ++ mt7530_led_config_of(phydev); ++ + return 0; + } + diff --git a/target/linux/generic/hack-6.12/770-r8169-LED-uses-original-network-port-name.patch b/target/linux/generic/hack-6.12/770-r8169-LED-uses-original-network-port-name.patch new file mode 100644 index 00000000000..55d4b481a24 --- /dev/null +++ b/target/linux/generic/hack-6.12/770-r8169-LED-uses-original-network-port-name.patch @@ -0,0 +1,89 @@ +From 5f8e52f3991c794be69af13170e5c54e5afe0674 Mon Sep 17 00:00:00 2001 +From: Chukun Pan +Date: Sun, 5 Jan 2025 18:08:29 +0800 +Subject: [PATCH] r8169: LED uses original network port name + +Most Linux distributions use Predictable Network Interface Names +to name PCIe network interfaces (systemd-udevd renames them). +Since OpenWrt doesn't use this, let's use the original network +port name in the driver to match the LED. + +Signed-off-by: Chukun Pan +--- + +--- a/drivers/net/ethernet/realtek/r8169.h ++++ b/drivers/net/ethernet/realtek/r8169.h +@@ -84,8 +84,6 @@ u8 rtl8168d_efuse_read(struct rtl8169_pr + void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, + enum mac_version ver); + +-void r8169_get_led_name(struct rtl8169_private *tp, int idx, +- char *buf, int buf_len); + int rtl8168_get_led_mode(struct rtl8169_private *tp); + int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val); + struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev); +--- a/drivers/net/ethernet/realtek/r8169_leds.c ++++ b/drivers/net/ethernet/realtek/r8169_leds.c +@@ -129,14 +129,13 @@ static struct device * + static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev, + struct net_device *ndev, int index) + { +- struct rtl8169_private *tp = netdev_priv(ndev); + struct led_classdev *led_cdev = &ldev->led; + char led_name[LED_MAX_NAME_SIZE]; + + ldev->ndev = ndev; + ldev->index = index; + +- r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE); ++ snprintf(led_name, sizeof(led_name), "%s-%d::lan", ndev->name, index); + led_cdev->name = led_name; + led_cdev->hw_control_trigger = "netdev"; + led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; +@@ -228,14 +227,13 @@ static int rtl8125_led_hw_control_get(st + static void rtl8125_setup_led_ldev(struct r8169_led_classdev *ldev, + struct net_device *ndev, int index) + { +- struct rtl8169_private *tp = netdev_priv(ndev); + struct led_classdev *led_cdev = &ldev->led; + char led_name[LED_MAX_NAME_SIZE]; + + ldev->ndev = ndev; + ldev->index = index; + +- r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE); ++ snprintf(led_name, sizeof(led_name), "%s-%d::lan", ndev->name, index); + led_cdev->name = led_name; + led_cdev->hw_control_trigger = "netdev"; + led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -980,28 +980,6 @@ int rtl8125_get_led_mode(struct rtl8169_ + return ret; + } + +-void r8169_get_led_name(struct rtl8169_private *tp, int idx, +- char *buf, int buf_len) +-{ +- struct pci_dev *pdev = tp->pci_dev; +- char pdom[8], pfun[8]; +- int domain; +- +- domain = pci_domain_nr(pdev->bus); +- if (domain) +- snprintf(pdom, sizeof(pdom), "P%d", domain); +- else +- pdom[0] = '\0'; +- +- if (pdev->multifunction) +- snprintf(pfun, sizeof(pfun), "f%d", PCI_FUNC(pdev->devfn)); +- else +- pfun[0] = '\0'; +- +- snprintf(buf, buf_len, "en%sp%ds%d%s-%d::lan", pdom, pdev->bus->number, +- PCI_SLOT(pdev->devfn), pfun, idx); +-} +- + static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type) + { + /* based on RTL8168FP_OOBMAC_BASE in vendor driver */ diff --git a/target/linux/generic/hack-6.12/773-bgmac-add-srab-switch.patch b/target/linux/generic/hack-6.12/773-bgmac-add-srab-switch.patch new file mode 100644 index 00000000000..40634f9ed0a --- /dev/null +++ b/target/linux/generic/hack-6.12/773-bgmac-add-srab-switch.patch @@ -0,0 +1,98 @@ +From 3cb240533ab787899dc7f17aa7d6c5b4810e2e58 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Fri, 7 Jul 2017 17:26:01 +0200 +Subject: bcm53xx: bgmac: use srab switch driver + +use the srab switch driver on these SoCs. + +Signed-off-by: Hauke Mehrtens +--- + drivers/net/ethernet/broadcom/bgmac-bcma.c | 1 + + drivers/net/ethernet/broadcom/bgmac.c | 24 ++++++++++++++++++++++++ + drivers/net/ethernet/broadcom/bgmac.h | 4 ++++ + 3 files changed, 29 insertions(+) + +--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c ++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c +@@ -280,6 +280,7 @@ static int bgmac_probe(struct bcma_devic + bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; + bgmac->feature_flags |= BGMAC_FEAT_NO_RESET; + bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500; ++ bgmac->feature_flags |= BGMAC_FEAT_SRAB; + break; + default: + bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; +--- a/drivers/net/ethernet/broadcom/bgmac.c ++++ b/drivers/net/ethernet/broadcom/bgmac.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1408,6 +1409,17 @@ static const struct ethtool_ops bgmac_et + .set_link_ksettings = phy_ethtool_set_link_ksettings, + }; + ++static struct b53_platform_data bgmac_b53_pdata = { ++}; ++ ++static struct platform_device bgmac_b53_dev = { ++ .name = "b53-srab-switch", ++ .id = -1, ++ .dev = { ++ .platform_data = &bgmac_b53_pdata, ++ }, ++}; ++ + /************************************************** + * MII + **************************************************/ +@@ -1546,6 +1558,14 @@ int bgmac_enet_probe(struct bgmac *bgmac + + bgmac->in_init = false; + ++ if ((bgmac->feature_flags & BGMAC_FEAT_SRAB) && !bgmac_b53_pdata.regs) { ++ bgmac_b53_pdata.regs = ioremap(0x18007000, 0x1000); ++ ++ err = platform_device_register(&bgmac_b53_dev); ++ if (!err) ++ bgmac->b53_device = &bgmac_b53_dev; ++ } ++ + err = register_netdev(bgmac->net_dev); + if (err) { + dev_err(bgmac->dev, "Cannot register net device\n"); +@@ -1568,6 +1588,10 @@ EXPORT_SYMBOL_GPL(bgmac_enet_probe); + + void bgmac_enet_remove(struct bgmac *bgmac) + { ++ if (bgmac->b53_device) ++ platform_device_unregister(&bgmac_b53_dev); ++ bgmac->b53_device = NULL; ++ + unregister_netdev(bgmac->net_dev); + phy_disconnect(bgmac->net_dev->phydev); + netif_napi_del(&bgmac->napi); +--- a/drivers/net/ethernet/broadcom/bgmac.h ++++ b/drivers/net/ethernet/broadcom/bgmac.h +@@ -387,6 +387,7 @@ + #define BGMAC_FEAT_CC4_IF_SW_TYPE_RGMII BIT(18) + #define BGMAC_FEAT_CC7_IF_TYPE_RGMII BIT(19) + #define BGMAC_FEAT_IDM_MASK BIT(20) ++#define BGMAC_FEAT_SRAB BIT(21) + + struct bgmac_slot_info { + union { +@@ -494,6 +495,9 @@ struct bgmac { + void (*cmn_maskset32)(struct bgmac *bgmac, u16 offset, u32 mask, + u32 set); + int (*phy_connect)(struct bgmac *bgmac); ++ ++ /* platform device for associated switch */ ++ struct platform_device *b53_device; + }; + + struct bgmac *bgmac_alloc(struct device *dev); diff --git a/target/linux/generic/hack-6.12/780-usb-net-MeigLink_modem_support.patch b/target/linux/generic/hack-6.12/780-usb-net-MeigLink_modem_support.patch new file mode 100644 index 00000000000..eff038fbd06 --- /dev/null +++ b/target/linux/generic/hack-6.12/780-usb-net-MeigLink_modem_support.patch @@ -0,0 +1,70 @@ +From f81700b6bb2eda3756247bce472d8eaf6f466f61 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:49:26 +0200 +Subject: [PATCH] net/usb/qmi_wwan: add MeigLink modem support + +--- + drivers/net/usb/qmi_wwan.c | 1 + + drivers/usb/serial/option.c | 7 +++++++ + 2 files changed, 8 insertions(+) + +--- a/drivers/net/usb/qmi_wwan.c ++++ b/drivers/net/usb/qmi_wwan.c +@@ -1082,6 +1082,11 @@ static const struct usb_device_id produc + USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7), + .driver_info = (unsigned long)&qmi_wwan_info, + }, ++ { /* Meiglink SGM828 */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d49, USB_CLASS_VENDOR_SPEC, 0x10, 0x05), ++ .driver_info = (unsigned long)&qmi_wwan_info, ++ }, ++ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0122)}, /* Quectel RG650V */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */ +@@ -1089,6 +1094,7 @@ static const struct usb_device_id produc + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)}, /* Quectel EM160R-GL */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0801)}, /* Quectel RM520N */ ++ {QMI_MATCH_FF_FF_FF(0x05c6, 0xf601)}, /* MeigLink SLM750 */ + + /* 3. Combined interface devices matching on interface number */ + {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */ +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -247,6 +247,11 @@ static void option_instat_callback(struc + #define UBLOX_PRODUCT_R410M 0x90b2 + /* These Yuga products use Qualcomm's vendor ID */ + #define YUGA_PRODUCT_CLM920_NC5 0x9625 ++/* These MeigLink products use Qualcomm's vendor ID */ ++#define MEIGLINK_PRODUCT_SLM750 0xf601 ++ ++#define MEIGLINK_VENDOR_ID 0x2dee ++#define MEIGLINK_PRODUCT_SLM828 0x4d49 + + #define QUECTEL_VENDOR_ID 0x2c7c + /* These Quectel products use Quectel's vendor ID */ +@@ -1156,6 +1161,11 @@ static const struct usb_device_id option + { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */ + { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000), /* SIMCom SIM5218 */ + .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) | NCTRL(3) | RSVD(4) }, ++ /* MeiG */ ++ { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x01) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x02) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x03) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x04) }, + /* Quectel products using Qualcomm vendor ID */ + { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)}, + { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20), +@@ -1197,6 +1207,11 @@ static const struct usb_device_id option + .driver_info = ZLP }, + { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96), + .driver_info = RSVD(4) }, ++ /* Meiglink products using Qualcomm vendor ID */ ++ // Works OK. In case of some issues check macros that are used by Quectel Products ++ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0xff, 0xff), ++ .driver_info = NUMEP2 }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff), + .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) }, diff --git a/target/linux/generic/hack-6.12/781-usb-net-rndis-support-asr.patch b/target/linux/generic/hack-6.12/781-usb-net-rndis-support-asr.patch new file mode 100644 index 00000000000..47339b6c22f --- /dev/null +++ b/target/linux/generic/hack-6.12/781-usb-net-rndis-support-asr.patch @@ -0,0 +1,69 @@ +From 9fabf60187f1fa19e6f6bb5441587d485bd534b0 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 9 Apr 2024 17:06:38 +0100 +Subject: [PATCH] rndis_host: add a bunch of USB IDs + +Add a bunch of USB IDs found in various places online to the +RNDIS USB network driver. + +Signed-off-by: Daniel Golle +--- + drivers/net/usb/rndis_host.c | 40 ++++++++++++++++++++++ + 1 file changed, 40 insertions(+) + +--- a/drivers/net/usb/rndis_host.c ++++ b/drivers/net/usb/rndis_host.c +@@ -630,6 +630,16 @@ static const struct driver_info zte_rndi + .tx_fixup = rndis_tx_fixup, + }; + ++static const struct driver_info asr_rndis_info = { ++ .description = "Asr RNDIS device", ++ .flags = FLAG_WWAN | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT | FLAG_NOARP, ++ .bind = rndis_bind, ++ .unbind = rndis_unbind, ++ .status = rndis_status, ++ .rx_fixup = rndis_rx_fixup, ++ .tx_fixup = rndis_tx_fixup, ++}; ++ + /*-------------------------------------------------------------------------*/ + + static const struct usb_device_id products [] = { +@@ -666,6 +676,36 @@ static const struct usb_device_id produc + USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3), + .driver_info = (unsigned long) &rndis_info, + }, { ++ /* Quectel EG060V rndis device */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x6004, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { ++ /* Quectel EC200A rndis device */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x6005, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { ++ /* Quectel EC200T rndis device */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x6026, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { ++ /* Simcom A7906E rndis device */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x1e0e, 0x9011, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { ++ /* Meig SLM770A */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d57, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { ++ /* Meig SLM828 */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d49, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { + /* Novatel Verizon USB730L */ + USB_INTERFACE_INFO(USB_CLASS_MISC, 4, 1), + .driver_info = (unsigned long) &rndis_info, diff --git a/target/linux/generic/hack-6.12/800-GPIO-add-named-gpio-exports.patch b/target/linux/generic/hack-6.12/800-GPIO-add-named-gpio-exports.patch new file mode 100644 index 00000000000..f9b540f0d60 --- /dev/null +++ b/target/linux/generic/hack-6.12/800-GPIO-add-named-gpio-exports.patch @@ -0,0 +1,172 @@ +From cc809a441d8f2924f785eb863dfa6aef47a25b0b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 12 Aug 2014 20:49:27 +0200 +Subject: [PATCH 30/36] GPIO: add named gpio exports + +Signed-off-by: John Crispin +--- a/drivers/gpio/gpiolib-of.c ++++ b/drivers/gpio/gpiolib-of.c +@@ -21,6 +21,8 @@ + + #include + #include ++#include ++#include + + #include "gpiolib.h" + #include "gpiolib-of.h" +@@ -1198,3 +1200,73 @@ void of_gpiochip_remove(struct gpio_chip + { + of_node_put(dev_of_node(&chip->gpiodev->dev)); + } ++ ++#ifdef CONFIG_GPIO_SYSFS ++ ++static const struct of_device_id gpio_export_ids[] = { ++ { .compatible = "gpio-export" }, ++ { /* sentinel */ } ++}; ++ ++static int of_gpio_export_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *cnp; ++ u32 val; ++ int nb = 0; ++ ++ for_each_child_of_node(np, cnp) { ++ const char *name = NULL; ++ int gpio; ++ bool dmc; ++ int max_gpio = 1; ++ int i; ++ ++ of_property_read_string(cnp, "gpio-export,name", &name); ++ ++ if (!name) ++ max_gpio = of_gpio_named_count(cnp, "gpios"); ++ ++ for (i = 0; i < max_gpio; i++) { ++ struct gpio_desc *desc; ++ unsigned flags = 0; ++ enum of_gpio_flags of_flags; ++ ++ desc = of_get_named_gpiod_flags(cnp, "gpios", i, &of_flags); ++ if (IS_ERR(desc)) ++ return PTR_ERR(desc); ++ gpio = desc_to_gpio(desc); ++ ++ if (of_flags & OF_GPIO_ACTIVE_LOW) ++ flags |= GPIOF_ACTIVE_LOW; ++ ++ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ else ++ flags |= GPIOF_IN; ++ ++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) ++ continue; ++ ++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); ++ gpio_export_with_name(gpio_to_desc(gpio), dmc, name); ++ nb++; ++ } ++ } ++ ++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_export_driver = { ++ .driver = { ++ .name = "gpio-export", ++ .of_match_table = of_match_ptr(gpio_export_ids), ++ }, ++ .probe = of_gpio_export_probe, ++}; ++ ++module_platform_driver(gpio_export_driver); ++ ++#endif +--- a/include/linux/gpio/consumer.h ++++ b/include/linux/gpio/consumer.h +@@ -628,7 +628,10 @@ static inline int devm_acpi_dev_add_driv + + #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) + ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); + int gpiod_export(struct gpio_desc *desc, bool direction_may_change); ++int gpio_export_with_name(struct gpio_desc *desc, bool direction_may_change, ++ const char *name); + int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); + void gpiod_unexport(struct gpio_desc *desc); +@@ -637,11 +640,25 @@ void gpiod_unexport(struct gpio_desc *de + + #include + ++static inline int __gpiod_export(struct gpio_desc *desc, ++ bool direction_may_change, ++ const char *name) ++{ ++ return -ENOSYS; ++} ++ + static inline int gpiod_export(struct gpio_desc *desc, + bool direction_may_change) + { + return -ENOSYS; + } ++ ++static inline int gpio_export_with_name(struct gpio_desc *desc, ++ bool direction_may_change, ++ const char *name) ++{ ++ return -ENOSYS; ++} + + static inline int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc) +--- a/drivers/gpio/gpiolib-sysfs.c ++++ b/drivers/gpio/gpiolib-sysfs.c +@@ -571,7 +571,7 @@ static struct class gpio_class = { + * Returns: + * 0 on success, or negative errno on failure. + */ +-int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) + { + const char *ioname = NULL; + struct gpio_device *gdev; +@@ -629,6 +629,8 @@ int gpiod_export(struct gpio_desc *desc, + offset = gpio_chip_hwgpio(desc); + if (guard.gc->names && guard.gc->names[offset]) + ioname = guard.gc->names[offset]; ++ if (name) ++ ioname = name; + + dev = device_create_with_groups(&gpio_class, &gdev->dev, + MKDEV(0, 0), data, gpio_groups, +@@ -650,8 +652,21 @@ err_unlock: + gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return status; + } ++EXPORT_SYMBOL_GPL(__gpiod_export); ++ ++int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++{ ++ return __gpiod_export(desc, direction_may_change, NULL); ++} + EXPORT_SYMBOL_GPL(gpiod_export); + ++int gpio_export_with_name(struct gpio_desc *desc, bool direction_may_change, ++ const char *name) ++{ ++ return __gpiod_export(desc, direction_may_change, name); ++} ++EXPORT_SYMBOL_GPL(gpio_export_with_name); ++ + static int match_export(struct device *dev, const void *desc) + { + struct gpiod_data *data = dev_get_drvdata(dev); diff --git a/target/linux/generic/hack-6.12/810-bcma-ssb-fallback-sprom.patch b/target/linux/generic/hack-6.12/810-bcma-ssb-fallback-sprom.patch new file mode 100644 index 00000000000..392c873b0ef --- /dev/null +++ b/target/linux/generic/hack-6.12/810-bcma-ssb-fallback-sprom.patch @@ -0,0 +1,182 @@ +From e4d708702e6c98f2111e33201a264d6788564cb2 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Fri, 12 May 2023 11:08:43 +0200 +Subject: [PATCH] ssb_sprom: add generic kernel support for Broadcom Fallback SPROMs + +--- + drivers/bcma/Kconfig | 4 ++++ + drivers/bcma/Makefile | 1 + + drivers/bcma/bcma_private.h | 1 + + drivers/bcma/main.c | 8 ++++++++ + drivers/bcma/sprom.c | 23 ++++++++++++++--------- + drivers/ssb/Kconfig | 5 +++++ + drivers/ssb/Makefile | 1 + + drivers/ssb/main.c | 8 ++++++++ + drivers/ssb/sprom.c | 12 +++++++++++- + drivers/ssb/ssb_private.h | 2 +- + 10 files changed, 54 insertions(+), 11 deletions(-) + +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -18,6 +18,10 @@ config BCMA_BLOCKIO + bool + default y + ++config BCMA_FALLBACK_SPROM ++ bool ++ default y ++ + config BCMA_HOST_PCI_POSSIBLE + bool + depends on PCI = y +--- a/drivers/bcma/Makefile ++++ b/drivers/bcma/Makefile +@@ -11,6 +11,7 @@ bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) + bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o + bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o + bcma-$(CONFIG_BCMA_DRIVER_GPIO) += driver_gpio.o ++bcma-$(CONFIG_BCMA_FALLBACK_SPROM) += fallback-sprom.o + bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o + bcma-$(CONFIG_BCMA_HOST_SOC) += host_soc.o + obj-$(CONFIG_BCMA) += bcma.o +--- a/drivers/bcma/bcma_private.h ++++ b/drivers/bcma/bcma_private.h +@@ -8,6 +8,7 @@ + + #include + #include ++#include "fallback-sprom.h" + + #define bcma_err(bus, fmt, ...) \ + dev_err((bus)->dev, "bus%d: " fmt, (bus)->num, ##__VA_ARGS__) +--- a/drivers/bcma/main.c ++++ b/drivers/bcma/main.c +@@ -677,6 +677,14 @@ static int __init bcma_modinit(void) + { + int err; + ++#ifdef CONFIG_BCMA_FALLBACK_SPROM ++ err = bcma_fbs_register(); ++ if (err) { ++ pr_err("Fallback SPROM initialization failed\n"); ++ err = 0; ++ } ++#endif /* CONFIG_BCMA_FALLBACK_SPROM */ ++ + err = bcma_init_bus_register(); + if (err) + return err; +--- a/drivers/bcma/sprom.c ++++ b/drivers/bcma/sprom.c +@@ -51,21 +51,26 @@ static int bcma_fill_sprom_with_fallback + { + int err; + +- if (!get_fallback_sprom) { ++ if (get_fallback_sprom) ++ err = get_fallback_sprom(bus, out); ++ ++#ifdef CONFIG_BCMA_FALLBACK_SPROM ++ if (!get_fallback_sprom || err) ++ err = bcma_get_fallback_sprom(bus, out); ++#else ++ if (!get_fallback_sprom) + err = -ENOENT; +- goto fail; +- } ++#endif /* CONFIG_BCMA_FALLBACK_SPROM */ + +- err = get_fallback_sprom(bus, out); +- if (err) +- goto fail; ++ if (err) { ++ bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err); ++ return err; ++ } + + bcma_debug(bus, "Using SPROM revision %d provided by platform.\n", + bus->sprom.revision); ++ + return 0; +-fail: +- bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err); +- return err; + } + + /************************************************** +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -25,6 +25,11 @@ if SSB + config SSB_SPROM + bool + ++config SSB_FALLBACK_SPROM ++ bool ++ depends on SSB_PCIHOST ++ default y ++ + # Support for Block-I/O. SELECT this from the driver that needs it. + config SSB_BLOCKIO + bool +--- a/drivers/ssb/Makefile ++++ b/drivers/ssb/Makefile +@@ -2,6 +2,7 @@ + # core + ssb-y += main.o scan.o + ssb-$(CONFIG_SSB_EMBEDDED) += embedded.o ++ssb-$(CONFIG_SSB_FALLBACK_SPROM) += fallback-sprom.o + ssb-$(CONFIG_SSB_SPROM) += sprom.o + + # host support +--- a/drivers/ssb/main.c ++++ b/drivers/ssb/main.c +@@ -1289,6 +1289,14 @@ static int __init ssb_modinit(void) + { + int err; + ++#ifdef CONFIG_SSB_FALLBACK_SPROM ++ err = ssb_fbs_register(); ++ if (err) { ++ pr_err("Fallback SPROM initialization failed\n"); ++ err = 0; ++ } ++#endif /* CONFIG_SSB_FALLBACK_SPROM */ ++ + /* See the comment at the ssb_is_early_boot definition */ + ssb_is_early_boot = 0; + err = bus_register(&ssb_bustype); +--- a/drivers/ssb/sprom.c ++++ b/drivers/ssb/sprom.c +@@ -180,10 +180,20 @@ int ssb_arch_register_fallback_sprom(int + + int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out) + { ++ int err; ++ ++ if (get_fallback_sprom) ++ err = get_fallback_sprom(bus, out); ++ ++#ifdef CONFIG_SSB_FALLBACK_SPROM ++ if (!get_fallback_sprom || err) ++ err = ssb_get_fallback_sprom(bus, out); ++#else + if (!get_fallback_sprom) + return -ENOENT; ++#endif /* CONFIG_SSB_FALLBACK_SPROM */ + +- return get_fallback_sprom(bus, out); ++ return err; + } + + /* https://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */ +--- a/drivers/ssb/ssb_private.h ++++ b/drivers/ssb/ssb_private.h +@@ -8,7 +8,7 @@ + #include + #include + #include +- ++#include "fallback-sprom.h" + + /* pci.c */ + #ifdef CONFIG_SSB_PCIHOST diff --git a/target/linux/generic/hack-6.12/901-debloat_sock_diag.patch b/target/linux/generic/hack-6.12/901-debloat_sock_diag.patch new file mode 100644 index 00000000000..17a22d0528a --- /dev/null +++ b/target/linux/generic/hack-6.12/901-debloat_sock_diag.patch @@ -0,0 +1,181 @@ +From 3b6115d6b57a263bdc8c9b1df273bd4a7955eead Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 8 Jul 2017 08:16:31 +0200 +Subject: debloat: add some debloat patches, strip down procfs and make O_DIRECT support optional, saves ~15K after lzma on MIPS + +Signed-off-by: Felix Fietkau +--- + net/Kconfig | 3 +++ + net/core/Makefile | 3 ++- + net/core/sock.c | 2 ++ + net/ipv4/Kconfig | 1 + + net/netlink/Kconfig | 1 + + net/packet/Kconfig | 1 + + net/unix/Kconfig | 1 + + 7 files changed, 11 insertions(+), 1 deletion(-) + +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -138,6 +138,9 @@ source "net/mptcp/Kconfig" + + endif # if INET + ++config SOCK_DIAG ++ bool ++ + config NETWORK_SECMARK + bool "Security Marking" + help +--- a/net/core/Makefile ++++ b/net/core/Makefile +@@ -11,12 +11,13 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core. + + obj-y += dev.o dev_addr_lists.o dst.o netevent.o \ + neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ +- sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \ ++ dev_ioctl.o tso.o sock_reuseport.o \ + fib_notifier.o xdp.o flow_offload.o gro.o \ + netdev-genl.o netdev-genl-gen.o gso.o + + obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o + ++obj-$(CONFIG_SOCK_DIAG) += sock_diag.o + obj-y += net-sysfs.o + obj-y += hotdata.o + obj-y += netdev_rx_queue.o +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -118,6 +118,7 @@ + #include + #include + #include ++#include + + #include + +@@ -152,6 +153,7 @@ + + static DEFINE_MUTEX(proto_list_mutex); + static LIST_HEAD(proto_list); ++DEFINE_COOKIE(sock_cookie); + + static void sock_def_write_space_wfree(struct sock *sk); + static void sock_def_write_space(struct sock *sk); +@@ -587,6 +589,21 @@ discard_and_relse: + } + EXPORT_SYMBOL(__sk_receive_skb); + ++u64 __sock_gen_cookie(struct sock *sk) ++{ ++ u64 res = atomic64_read(&sk->sk_cookie); ++ ++ if (!res) { ++ u64 new = gen_cookie_next(&sock_cookie); ++ ++ atomic64_cmpxchg(&sk->sk_cookie, res, new); ++ ++ /* Another thread might have changed sk_cookie before us. */ ++ res = atomic64_read(&sk->sk_cookie); ++ } ++ return res; ++} ++ + INDIRECT_CALLABLE_DECLARE(struct dst_entry *ip6_dst_check(struct dst_entry *, + u32)); + INDIRECT_CALLABLE_DECLARE(struct dst_entry *ipv4_dst_check(struct dst_entry *, +@@ -2342,9 +2359,11 @@ static void __sk_free(struct sock *sk) + if (likely(sk->sk_net_refcnt)) + sock_inuse_add(sock_net(sk), -1); + ++#ifdef CONFIG_SOCK_DIAG + if (unlikely(sk->sk_net_refcnt && sock_diag_has_destroy_listeners(sk))) + sock_diag_broadcast_destroy(sk); + else ++#endif + sk_destruct(sk); + } + +--- a/net/core/sock_diag.c ++++ b/net/core/sock_diag.c +@@ -12,7 +12,6 @@ + #include + #include + #include +-#include + #include + #include + +@@ -22,23 +21,6 @@ static const struct sock_diag_inet_compa + + static struct workqueue_struct *broadcast_wq; + +-DEFINE_COOKIE(sock_cookie); +- +-u64 __sock_gen_cookie(struct sock *sk) +-{ +- u64 res = atomic64_read(&sk->sk_cookie); +- +- if (!res) { +- u64 new = gen_cookie_next(&sock_cookie); +- +- atomic64_cmpxchg(&sk->sk_cookie, res, new); +- +- /* Another thread might have changed sk_cookie before us. */ +- res = atomic64_read(&sk->sk_cookie); +- } +- return res; +-} +- + int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie) + { + u64 res; +--- a/net/ipv4/Kconfig ++++ b/net/ipv4/Kconfig +@@ -423,6 +423,7 @@ config INET_TUNNEL + + config INET_DIAG + tristate "INET: socket monitoring interface" ++ select SOCK_DIAG + default y + help + Support for INET (TCP, DCCP, etc) socket monitoring interface used by +--- a/net/netlink/Kconfig ++++ b/net/netlink/Kconfig +@@ -5,6 +5,7 @@ + + config NETLINK_DIAG + tristate "NETLINK: socket monitoring interface" ++ select SOCK_DIAG + default n + help + Support for NETLINK socket monitoring interface used by the ss tool. +--- a/net/packet/Kconfig ++++ b/net/packet/Kconfig +@@ -19,6 +19,7 @@ config PACKET + config PACKET_DIAG + tristate "Packet: sockets monitoring interface" + depends on PACKET ++ select SOCK_DIAG + default n + help + Support for PF_PACKET sockets monitoring interface used by the ss tool. +--- a/net/unix/Kconfig ++++ b/net/unix/Kconfig +@@ -24,6 +24,7 @@ config AF_UNIX_OOB + config UNIX_DIAG + tristate "UNIX: socket monitoring interface" + depends on UNIX ++ select SOCK_DIAG + default n + help + Support for UNIX socket monitoring interface used by the ss tool. +--- a/net/xdp/Kconfig ++++ b/net/xdp/Kconfig +@@ -10,6 +10,7 @@ config XDP_SOCKETS + config XDP_SOCKETS_DIAG + tristate "XDP sockets: monitoring interface" + depends on XDP_SOCKETS ++ select SOCK_DIAG + default n + help + Support for PF_XDP sockets monitoring interface used by the ss tool. diff --git a/target/linux/generic/hack-6.12/902-debloat_proc.patch b/target/linux/generic/hack-6.12/902-debloat_proc.patch new file mode 100644 index 00000000000..e3e2defa96a --- /dev/null +++ b/target/linux/generic/hack-6.12/902-debloat_proc.patch @@ -0,0 +1,420 @@ +From 9e3f1d0805b2d919904dd9a4ff0d956314cc3cba Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 8 Jul 2017 08:20:09 +0200 +Subject: debloat: procfs + +Signed-off-by: Felix Fietkau +--- + fs/locks.c | 2 ++ + fs/proc/Kconfig | 5 +++++ + fs/proc/consoles.c | 3 +++ + fs/proc/proc_tty.c | 11 ++++++++++- + include/net/snmp.h | 18 +++++++++++++++++- + ipc/msg.c | 3 +++ + ipc/sem.c | 2 ++ + ipc/shm.c | 2 ++ + ipc/util.c | 3 +++ + kernel/exec_domain.c | 2 ++ + kernel/irq/proc.c | 9 +++++++++ + kernel/time/timer_list.c | 2 ++ + mm/vmalloc.c | 2 ++ + mm/vmstat.c | 8 +++++--- + net/8021q/vlanproc.c | 6 ++++++ + net/core/net-procfs.c | 18 ++++++++++++------ + net/core/sock.c | 2 ++ + net/ipv4/fib_trie.c | 18 ++++++++++++------ + net/ipv4/proc.c | 3 +++ + net/ipv4/route.c | 3 +++ + 20 files changed, 105 insertions(+), 17 deletions(-) + +--- a/fs/locks.c ++++ b/fs/locks.c +@@ -2979,6 +2979,8 @@ static const struct seq_operations locks + + static int __init proc_locks_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + proc_create_seq_private("locks", 0, NULL, &locks_seq_operations, + sizeof(struct locks_iterator), NULL); + return 0; +--- a/fs/proc/Kconfig ++++ b/fs/proc/Kconfig +@@ -101,6 +101,11 @@ config PROC_CHILDREN + Say Y if you are running any user-space software which takes benefit from + this interface. For example, rkt is such a piece of software. + ++config PROC_STRIPPED ++ default n ++ depends on EXPERT ++ bool "Strip non-essential /proc functionality to reduce code size" ++ + config PROC_PID_ARCH_STATUS + def_bool n + depends on PROC_FS +--- a/fs/proc/consoles.c ++++ b/fs/proc/consoles.c +@@ -110,6 +110,9 @@ static const struct seq_operations conso + + static int __init proc_consoles_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + proc_create_seq("consoles", 0, NULL, &consoles_op); + return 0; + } +--- a/fs/proc/proc_tty.c ++++ b/fs/proc/proc_tty.c +@@ -131,7 +131,10 @@ static const struct seq_operations tty_d + void proc_tty_register_driver(struct tty_driver *driver) + { + struct proc_dir_entry *ent; +- ++ ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + if (!driver->driver_name || driver->proc_entry || + !driver->ops->proc_show) + return; +@@ -148,6 +151,9 @@ void proc_tty_unregister_driver(struct t + { + struct proc_dir_entry *ent; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + ent = driver->proc_entry; + if (!ent) + return; +@@ -162,6 +168,9 @@ void proc_tty_unregister_driver(struct t + */ + void __init proc_tty_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + if (!proc_mkdir("tty", NULL)) + return; + proc_mkdir("tty/ldisc", NULL); /* Preserved: it's userspace visible */ +--- a/include/net/snmp.h ++++ b/include/net/snmp.h +@@ -124,6 +124,21 @@ struct linux_tls_mib { + #define DECLARE_SNMP_STAT(type, name) \ + extern __typeof__(type) __percpu *name + ++#ifdef CONFIG_PROC_STRIPPED ++#define __SNMP_STATS_DUMMY(mib) \ ++ do { (void) mib->mibs[0]; } while(0) ++ ++#define __SNMP_INC_STATS(mib, field) __SNMP_STATS_DUMMY(mib) ++#define SNMP_INC_STATS_ATOMIC_LONG(mib, field) __SNMP_STATS_DUMMY(mib) ++#define SNMP_INC_STATS(mib, field) __SNMP_STATS_DUMMY(mib) ++#define SNMP_DEC_STATS(mib, field) __SNMP_STATS_DUMMY(mib) ++#define __SNMP_ADD_STATS(mib, field, addend) __SNMP_STATS_DUMMY(mib) ++#define SNMP_ADD_STATS(mib, field, addend) __SNMP_STATS_DUMMY(mib) ++#define SNMP_UPD_PO_STATS(mib, basefield, addend) __SNMP_STATS_DUMMY(mib) ++#define __SNMP_UPD_PO_STATS(mib, basefield, addend) __SNMP_STATS_DUMMY(mib) ++ ++#else ++ + #define __SNMP_INC_STATS(mib, field) \ + __this_cpu_inc(mib->mibs[field]) + +@@ -154,8 +169,9 @@ struct linux_tls_mib { + __this_cpu_add(ptr[basefield##OCTETS], addend); \ + } while (0) + ++#endif + +-#if BITS_PER_LONG==32 ++#if (BITS_PER_LONG==32) && !defined(CONFIG_PROC_STRIPPED) + + #define __SNMP_ADD_STATS64(mib, field, addend) \ + do { \ +--- a/ipc/msg.c ++++ b/ipc/msg.c +@@ -1370,6 +1370,9 @@ void __init msg_init(void) + { + msg_init_ns(&init_ipc_ns); + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + ipc_init_proc_interface("sysvipc/msg", + " key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n", + IPC_MSG_IDS, sysvipc_msg_proc_show); +--- a/ipc/sem.c ++++ b/ipc/sem.c +@@ -268,6 +268,8 @@ void sem_exit_ns(struct ipc_namespace *n + void __init sem_init(void) + { + sem_init_ns(&init_ipc_ns); ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; + ipc_init_proc_interface("sysvipc/sem", + " key semid perms nsems uid gid cuid cgid otime ctime\n", + IPC_SEM_IDS, sysvipc_sem_proc_show); +--- a/ipc/shm.c ++++ b/ipc/shm.c +@@ -155,6 +155,8 @@ pure_initcall(ipc_ns_init); + + void __init shm_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; + ipc_init_proc_interface("sysvipc/shm", + #if BITS_PER_LONG <= 32 + " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", +--- a/ipc/util.c ++++ b/ipc/util.c +@@ -141,6 +141,9 @@ void __init ipc_init_proc_interface(cons + struct proc_dir_entry *pde; + struct ipc_proc_iface *iface; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + iface = kmalloc(sizeof(*iface), GFP_KERNEL); + if (!iface) + return; +--- a/kernel/exec_domain.c ++++ b/kernel/exec_domain.c +@@ -29,6 +29,8 @@ static int execdomains_proc_show(struct + + static int __init proc_execdomains_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + proc_create_single("execdomains", 0, NULL, execdomains_proc_show); + return 0; + } +--- a/kernel/irq/proc.c ++++ b/kernel/irq/proc.c +@@ -339,6 +339,9 @@ void register_irq_proc(unsigned int irq, + void __maybe_unused *irqp = (void *)(unsigned long) irq; + char name [MAX_NAMELEN]; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP)) ++ return; ++ + if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip)) + return; + +@@ -397,6 +400,9 @@ void unregister_irq_proc(unsigned int ir + { + char name [MAX_NAMELEN]; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP)) ++ return; ++ + if (!root_irq_dir || !desc->dir) + return; + #ifdef CONFIG_SMP +@@ -435,6 +441,9 @@ void init_irq_proc(void) + unsigned int irq; + struct irq_desc *desc; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP)) ++ return; ++ + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", NULL); + if (!root_irq_dir) +--- a/kernel/time/timer_list.c ++++ b/kernel/time/timer_list.c +@@ -354,6 +354,8 @@ static int __init init_timer_list_procfs + { + struct proc_dir_entry *pe; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + pe = proc_create_seq_private("timer_list", 0400, NULL, &timer_list_sops, + sizeof(struct timer_list_iter), NULL); + if (!pe) +--- a/mm/vmalloc.c ++++ b/mm/vmalloc.c +@@ -5079,6 +5079,9 @@ static int vmalloc_info_show(struct seq_ + + static int __init proc_vmalloc_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + proc_create_single("vmallocinfo", 0400, NULL, vmalloc_info_show); + return 0; + } +--- a/mm/vmstat.c ++++ b/mm/vmstat.c +@@ -2197,10 +2197,12 @@ void __init init_mm_internals(void) + start_shepherd_timer(); + #endif + #ifdef CONFIG_PROC_FS +- proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op); +- proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) { ++ proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op); ++ proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op); ++ proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op); ++ } + proc_create_seq("vmstat", 0444, NULL, &vmstat_op); +- proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op); + #endif + } + +--- a/net/8021q/vlanproc.c ++++ b/net/8021q/vlanproc.c +@@ -93,6 +93,9 @@ void vlan_proc_cleanup(struct net *net) + { + struct vlan_net *vn = net_generic(net, vlan_net_id); + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + if (vn->proc_vlan_conf) + remove_proc_entry(name_conf, vn->proc_vlan_dir); + +@@ -112,6 +115,9 @@ int __net_init vlan_proc_init(struct net + { + struct vlan_net *vn = net_generic(net, vlan_net_id); + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + vn->proc_vlan_dir = proc_net_mkdir(net, name_root, net->proc_net); + if (!vn->proc_vlan_dir) + goto err; +--- a/net/core/net-procfs.c ++++ b/net/core/net-procfs.c +@@ -295,10 +295,12 @@ static int __net_init dev_proc_net_init( + if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops, + sizeof(struct seq_net_private))) + goto out; +- if (!proc_create_seq("softnet_stat", 0444, net->proc_net, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_seq("softnet_stat", 0444, net->proc_net, + &softnet_seq_ops)) + goto out_dev; +- if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops, + sizeof(struct seq_net_private))) + goto out_softnet; + +@@ -308,9 +310,11 @@ static int __net_init dev_proc_net_init( + out: + return rc; + out_ptype: +- remove_proc_entry("ptype", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("ptype", net->proc_net); + out_softnet: +- remove_proc_entry("softnet_stat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("softnet_stat", net->proc_net); + out_dev: + remove_proc_entry("dev", net->proc_net); + goto out; +@@ -320,8 +324,10 @@ static void __net_exit dev_proc_net_exit + { + wext_proc_exit(net); + +- remove_proc_entry("ptype", net->proc_net); +- remove_proc_entry("softnet_stat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) { ++ remove_proc_entry("ptype", net->proc_net); ++ remove_proc_entry("softnet_stat", net->proc_net); ++ } + remove_proc_entry("dev", net->proc_net); + } + +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -4285,6 +4285,8 @@ static __net_initdata struct pernet_oper + + static int __init proto_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + return register_pernet_subsys(&proto_net_ops); + } + +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -3016,11 +3016,13 @@ static const struct seq_operations fib_r + + int __net_init fib_proc_init(struct net *net) + { +- if (!proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops, + sizeof(struct fib_trie_iter))) + goto out1; + +- if (!proc_create_net_single("fib_triestat", 0444, net->proc_net, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_net_single("fib_triestat", 0444, net->proc_net, + fib_triestat_seq_show, NULL)) + goto out2; + +@@ -3031,17 +3033,21 @@ int __net_init fib_proc_init(struct net + return 0; + + out3: +- remove_proc_entry("fib_triestat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("fib_triestat", net->proc_net); + out2: +- remove_proc_entry("fib_trie", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("fib_trie", net->proc_net); + out1: + return -ENOMEM; + } + + void __net_exit fib_proc_exit(struct net *net) + { +- remove_proc_entry("fib_trie", net->proc_net); +- remove_proc_entry("fib_triestat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) { ++ remove_proc_entry("fib_trie", net->proc_net); ++ remove_proc_entry("fib_triestat", net->proc_net); ++ } + remove_proc_entry("route", net->proc_net); + } + +--- a/net/ipv4/proc.c ++++ b/net/ipv4/proc.c +@@ -563,5 +563,8 @@ static __net_initdata struct pernet_oper + + int __init ip_misc_proc_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + return register_pernet_subsys(&ip_proc_ops); + } +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -382,6 +382,9 @@ static struct pernet_operations ip_rt_pr + + static int __init ip_rt_proc_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + return register_pernet_subsys(&ip_rt_proc_ops); + } + +--- a/net/ipv4/inet_timewait_sock.c ++++ b/net/ipv4/inet_timewait_sock.c +@@ -285,7 +285,7 @@ void __inet_twsk_schedule(struct inet_ti + */ + + if (!rearm) { +- bool kill = timeo <= 4*HZ; ++ bool __maybe_unused kill = timeo <= 4*HZ; + + __NET_INC_STATS(twsk_net(tw), kill ? LINUX_MIB_TIMEWAITKILLED : + LINUX_MIB_TIMEWAITED); diff --git a/target/linux/generic/hack-6.12/904-debloat_dma_buf.patch b/target/linux/generic/hack-6.12/904-debloat_dma_buf.patch new file mode 100644 index 00000000000..af566ec9642 --- /dev/null +++ b/target/linux/generic/hack-6.12/904-debloat_dma_buf.patch @@ -0,0 +1,105 @@ +From e3692cb2fcd5ba1244512a0f43b8118f65f1c375 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 8 Jul 2017 08:20:43 +0200 +Subject: debloat: dmabuf + +Signed-off-by: Felix Fietkau +--- + drivers/base/Kconfig | 2 +- + drivers/dma-buf/Makefile | 10 +++++++--- + drivers/dma-buf/dma-buf.c | 4 +++- + kernel/sched/core.c | 1 + + net/Kconfig | 2 +- + 5 files changed, 13 insertions(+), 6 deletions(-) + +--- a/drivers/base/Kconfig ++++ b/drivers/base/Kconfig +@@ -198,7 +198,7 @@ config SOC_BUS + source "drivers/base/regmap/Kconfig" + + config DMA_SHARED_BUFFER +- bool ++ tristate + default n + select IRQ_WORK + help +--- a/drivers/dma-buf/heaps/Makefile ++++ b/drivers/dma-buf/heaps/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 +-obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o +-obj-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o ++dma-buf-objs-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o ++dma-buf-objs-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o +--- a/drivers/dma-buf/Makefile ++++ b/drivers/dma-buf/Makefile +@@ -1,12 +1,14 @@ + # SPDX-License-Identifier: GPL-2.0-only +-obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ ++obj-$(CONFIG_DMA_SHARED_BUFFER) := dma-shared-buffer.o ++ ++dma-buf-objs-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ + dma-fence-unwrap.o dma-resv.o +-obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o +-obj-$(CONFIG_DMABUF_HEAPS) += heaps/ +-obj-$(CONFIG_SYNC_FILE) += sync_file.o +-obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o +-obj-$(CONFIG_UDMABUF) += udmabuf.o +-obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o ++dma-buf-objs-$(CONFIG_DMABUF_HEAPS) += dma-heap.o ++obj-$(CONFIG_DMABUF_HEAPS) += heaps/ ++dma-buf-objs-$(CONFIG_SYNC_FILE) += sync_file.o ++dma-buf-objs-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o ++dma-buf-objs-$(CONFIG_UDMABUF) += udmabuf.o ++dma-buf-objs-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o + + dmabuf_selftests-y := \ + selftest.o \ +@@ -15,4 +17,6 @@ dmabuf_selftests-y := \ + st-dma-fence-unwrap.o \ + st-dma-resv.o + +-obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o ++dma-buf-objs-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o ++ ++dma-shared-buffer-objs := $(dma-buf-objs-y) +--- a/drivers/dma-buf/dma-buf.c ++++ b/drivers/dma-buf/dma-buf.c +@@ -1743,4 +1743,5 @@ static void __exit dma_buf_deinit(void) + kern_unmount(dma_buf_mnt); + dma_buf_uninit_sysfs_statistics(); + } +-__exitcall(dma_buf_deinit); ++module_exit(dma_buf_deinit); ++MODULE_LICENSE("GPL"); +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -4434,6 +4434,7 @@ int wake_up_state(struct task_struct *p, + { + return try_to_wake_up(p, state, 0); + } ++EXPORT_SYMBOL_GPL(wake_up_state); + + /* + * Perform scheduler related setup for a newly forked process p. +--- a/fs/d_path.c ++++ b/fs/d_path.c +@@ -314,6 +314,7 @@ char *dynamic_dname(char *buffer, int bu + buffer += buflen - sz; + return memcpy(buffer, temp, sz); + } ++EXPORT_SYMBOL_GPL(dynamic_dname); + + char *simple_dname(struct dentry *dentry, char *buffer, int buflen) + { +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -74,7 +74,7 @@ config SKB_EXTENSIONS + + config NET_DEVMEM + def_bool y +- depends on DMA_SHARED_BUFFER ++ depends on DMA_SHARED_BUFFER=y + depends on GENERIC_ALLOCATOR + depends on PAGE_POOL + diff --git a/target/linux/generic/hack-6.12/910-kobject_uevent.patch b/target/linux/generic/hack-6.12/910-kobject_uevent.patch new file mode 100644 index 00000000000..99dd5a70537 --- /dev/null +++ b/target/linux/generic/hack-6.12/910-kobject_uevent.patch @@ -0,0 +1,35 @@ +From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sun, 16 Jul 2017 16:56:10 +0200 +Subject: lib: add uevent_next_seqnum() + +Signed-off-by: Felix Fietkau +--- + include/linux/kobject.h | 2 ++ + lib/kobject_uevent.c | 6 ++++++ + 2 files changed, 8 insertions(+) + +--- a/include/linux/kobject.h ++++ b/include/linux/kobject.h +@@ -219,4 +219,6 @@ int kobject_synth_uevent(struct kobject + __printf(2, 3) + int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...); + ++u64 uevent_next_seqnum(void); ++ + #endif /* _KOBJECT_H_ */ +--- a/lib/kobject_uevent.c ++++ b/lib/kobject_uevent.c +@@ -178,6 +178,12 @@ out: + return r; + } + ++u64 uevent_next_seqnum(void) ++{ ++ return atomic64_inc_return(&uevent_seqnum); ++} ++EXPORT_SYMBOL_GPL(uevent_next_seqnum); ++ + /** + * kobject_synth_uevent - send synthetic uevent with arguments + * diff --git a/target/linux/generic/hack-6.12/911-kobject_add_broadcast_uevent.patch b/target/linux/generic/hack-6.12/911-kobject_add_broadcast_uevent.patch new file mode 100644 index 00000000000..df160baff60 --- /dev/null +++ b/target/linux/generic/hack-6.12/911-kobject_add_broadcast_uevent.patch @@ -0,0 +1,76 @@ +From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sun, 16 Jul 2017 16:56:10 +0200 +Subject: lib: add broadcast_uevent() + +Signed-off-by: Felix Fietkau +--- + include/linux/kobject.h | 5 +++++ + lib/kobject_uevent.c | 37 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 42 insertions(+) + +--- a/include/linux/kobject.h ++++ b/include/linux/kobject.h +@@ -32,6 +32,8 @@ + #define UEVENT_NUM_ENVP 64 /* number of env pointers */ + #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ + ++struct sk_buff; ++ + #ifdef CONFIG_UEVENT_HELPER + /* path to the userspace helper executed on an event */ + extern char uevent_helper[]; +@@ -221,4 +223,7 @@ int add_uevent_var(struct kobj_uevent_en + + u64 uevent_next_seqnum(void); + ++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, ++ gfp_t allocation); ++ + #endif /* _KOBJECT_H_ */ +--- a/lib/kobject_uevent.c ++++ b/lib/kobject_uevent.c +@@ -699,6 +699,43 @@ int add_uevent_var(struct kobj_uevent_en + EXPORT_SYMBOL_GPL(add_uevent_var); + + #if defined(CONFIG_NET) ++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, ++ gfp_t allocation) ++{ ++ struct uevent_sock *ue_sk; ++ int err = 0; ++ ++ /* send netlink message */ ++ mutex_lock(&uevent_sock_mutex); ++ list_for_each_entry(ue_sk, &uevent_sock_list, list) { ++ struct sock *uevent_sock = ue_sk->sk; ++ struct sk_buff *skb2; ++ ++ skb2 = skb_clone(skb, allocation); ++ if (!skb2) ++ break; ++ ++ err = netlink_broadcast(uevent_sock, skb2, pid, group, ++ allocation); ++ if (err) ++ break; ++ } ++ mutex_unlock(&uevent_sock_mutex); ++ ++ kfree_skb(skb); ++ return err; ++} ++#else ++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, ++ gfp_t allocation) ++{ ++ kfree_skb(skb); ++ return 0; ++} ++#endif ++EXPORT_SYMBOL_GPL(broadcast_uevent); ++ ++#if defined(CONFIG_NET) + static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb, + struct netlink_ext_ack *extack) + { diff --git a/target/linux/generic/hack-6.12/920-device_tree_cmdline.patch b/target/linux/generic/hack-6.12/920-device_tree_cmdline.patch new file mode 100644 index 00000000000..4763e82421e --- /dev/null +++ b/target/linux/generic/hack-6.12/920-device_tree_cmdline.patch @@ -0,0 +1,21 @@ +From e08bcbbaa52fcc41f02743fd2e62a33255ce52da Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:52:28 +0200 +Subject: [PATCH] of/ftd: add device tree cmdline + +--- + drivers/of/fdt.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -1049,6 +1049,9 @@ int __init early_init_dt_scan_chosen(cha + p = of_get_flat_dt_prop(node, "bootargs", &l); + if (p != NULL && l > 0) + strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE)); ++ p = of_get_flat_dt_prop(node, "bootargs-append", &l); ++ if (p != NULL && l > 0) ++ strlcat(cmdline, p, min_t(int, strlen(cmdline) + (int)l, COMMAND_LINE_SIZE)); + + handle_cmdline: + /* diff --git a/target/linux/generic/hack-6.12/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch b/target/linux/generic/hack-6.12/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch new file mode 100644 index 00000000000..a7c8a8efd2a --- /dev/null +++ b/target/linux/generic/hack-6.12/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch @@ -0,0 +1,30 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 19 Jul 2022 06:17:48 +0200 +Subject: [PATCH] Revert "Revert "Revert "driver core: Set fw_devlink=on by + default""" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit ea718c699055c8566eb64432388a04974c43b2ea. + +With of_platform_populate() called for MTD partitions that commit breaks +probing devices which reference MTD in device tree. + +Link: https://lore.kernel.org/all/696cb2da-20b9-b3dd-46d9-de4bf91a1506@gmail.com/T/#u +Signed-off-by: Rafał Miłecki +--- + drivers/base/core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -1653,7 +1653,7 @@ static void device_links_purge(struct de + #define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \ + DL_FLAG_PM_RUNTIME) + +-static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_RPM; ++static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE; + static int __init fw_devlink_setup(char *arg) + { + if (!arg) diff --git a/target/linux/generic/pending-6.12/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch b/target/linux/generic/pending-6.12/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch new file mode 100644 index 00000000000..725e223495b --- /dev/null +++ b/target/linux/generic/pending-6.12/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau +Date: Thu, 22 Oct 2020 22:00:03 +0200 +Subject: [PATCH] compiler.h: only include asm/rwonce.h for kernel code + +This header file is not in uapi, which makes any user space code that includes +linux/compiler.h to fail with the error 'asm/rwonce.h: No such file or directory' + +Fixes: e506ea451254 ("compiler.h: Split {READ,WRITE}_ONCE definitions out into rwonce.h") +Signed-off-by: Felix Fietkau +--- + +--- a/include/linux/compiler.h ++++ b/include/linux/compiler.h +@@ -191,6 +191,8 @@ void ftrace_likely_update(struct ftrace_ + __v; \ + }) + ++#include ++ + #endif /* __KERNEL__ */ + + /** +@@ -298,6 +300,4 @@ static inline void *offset_to_ptr(const + */ + #define prevent_tail_call_optimization() mb() + +-#include +- + #endif /* __LINUX_COMPILER_H */ diff --git a/target/linux/generic/pending-6.12/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch b/target/linux/generic/pending-6.12/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch new file mode 100644 index 00000000000..d79d03defb3 --- /dev/null +++ b/target/linux/generic/pending-6.12/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch @@ -0,0 +1,57 @@ +From: Felix Fietkau +Date: Wed, 18 Apr 2018 10:50:05 +0200 +Subject: [PATCH] MIPS: only process negative stack offsets on stack traces + +Fixes endless back traces in cases where the compiler emits a stack +pointer increase in a branch delay slot (probably for some form of +function return). + +[ 3.475442] BUG: MAX_STACK_TRACE_ENTRIES too low! +[ 3.480070] turning off the locking correctness validator. +[ 3.485521] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.14.34 #0 +[ 3.491475] Stack : 00000000 00000000 00000000 00000000 80e0fce2 00000034 00000000 00000000 +[ 3.499764] 87c3838c 80696377 8061047c 00000000 00000001 00000001 87c2d850 6534689f +[ 3.508059] 00000000 00000000 80e10000 00000000 00000000 000000cf 0000000f 00000000 +[ 3.516353] 00000000 806a0000 00076891 00000000 00000000 00000000 ffffffff 00000000 +[ 3.524648] 806c0000 00000004 80e10000 806a0000 00000003 80690000 00000000 80700000 +[ 3.532942] ... +[ 3.535362] Call Trace: +[ 3.537818] [<80010a48>] show_stack+0x58/0x100 +[ 3.542207] [<804c2f78>] dump_stack+0xe8/0x170 +[ 3.546613] [<80079f90>] save_trace+0xf0/0x110 +[ 3.551010] [<8007b1ec>] mark_lock+0x33c/0x78c +[ 3.555413] [<8007bf48>] __lock_acquire+0x2ac/0x1a08 +[ 3.560337] [<8007de60>] lock_acquire+0x64/0x8c +[ 3.564846] [<804e1570>] _raw_spin_lock_irqsave+0x54/0x78 +[ 3.570186] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.574770] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.579257] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.583839] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.588329] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.592911] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.597401] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.601983] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.606473] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.611055] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.615545] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.620125] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.624619] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.629197] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.633691] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.638269] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.642763] [<801b618c>] kernfs_notify+0x94/0xac + +Signed-off-by: Felix Fietkau +--- + +--- a/arch/mips/kernel/process.c ++++ b/arch/mips/kernel/process.c +@@ -395,6 +395,8 @@ static inline int is_sp_move_ins(union m + + if (ip->i_format.opcode == addiu_op || + ip->i_format.opcode == daddiu_op) { ++ if (ip->i_format.simmediate > 0) ++ return 0; + *frame_size = -ip->i_format.simmediate; + return 1; + } diff --git a/target/linux/generic/pending-6.12/103-kbuild-export-SUBARCH.patch b/target/linux/generic/pending-6.12/103-kbuild-export-SUBARCH.patch new file mode 100644 index 00000000000..0cedbeca40b --- /dev/null +++ b/target/linux/generic/pending-6.12/103-kbuild-export-SUBARCH.patch @@ -0,0 +1,21 @@ +From 173019b66dcc9d68ad9333aa744dad1e369b5aa8 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sun, 9 Jul 2017 00:26:53 +0200 +Subject: [PATCH 34/34] kernel: add compile fix for linux 4.9 on x86 + +Signed-off-by: Felix Fietkau +--- + Makefile | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/Makefile ++++ b/Makefile +@@ -589,7 +589,7 @@ export RUSTC_BOOTSTRAP := 1 + # Allows finding `.clippy.toml` in out-of-srctree builds. + export CLIPPY_CONF_DIR := $(srctree) + +-export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG ++export ARCH SRCARCH SUBARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG + export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN + export HOSTRUSTC KBUILD_HOSTRUSTFLAGS + export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL diff --git a/target/linux/generic/pending-6.12/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch b/target/linux/generic/pending-6.12/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch new file mode 100644 index 00000000000..9dd90eecdc8 --- /dev/null +++ b/target/linux/generic/pending-6.12/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch @@ -0,0 +1,75 @@ +From bd1b9f66d5134e518419f4c4dacf1884c1616983 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Thu, 28 Apr 2022 11:13:23 +0200 +Subject: [PATCH] watchdog: max63xx_wdt: Add support for specifying WDI logic + via GPIO +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On some boards is WDI logic of max6370 chip connected via GPIO. +So extend max63xx_wdt driver to allow specifying WDI logic via GPIO. + +Signed-off-by: Pali Rohár +--- + drivers/watchdog/max63xx_wdt.c | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +--- a/drivers/watchdog/max63xx_wdt.c ++++ b/drivers/watchdog/max63xx_wdt.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + #define DEFAULT_HEARTBEAT 60 + #define MAX_HEARTBEAT 60 +@@ -50,6 +51,9 @@ struct max63xx_wdt { + void __iomem *base; + spinlock_t lock; + ++ /* GPIOs */ ++ struct gpio_desc *gpio_wdi; ++ + /* WDI and WSET bits write access routines */ + void (*ping)(struct max63xx_wdt *wdt); + void (*set)(struct max63xx_wdt *wdt, u8 set); +@@ -155,6 +159,17 @@ static const struct watchdog_info max63x + .identity = "max63xx Watchdog", + }; + ++static void max63xx_gpio_ping(struct max63xx_wdt *wdt) ++{ ++ spin_lock(&wdt->lock); ++ ++ gpiod_set_value(wdt->gpio_wdi, 1); ++ udelay(1); ++ gpiod_set_value(wdt->gpio_wdi, 0); ++ ++ spin_unlock(&wdt->lock); ++} ++ + static void max63xx_mmap_ping(struct max63xx_wdt *wdt) + { + u8 val; +@@ -222,10 +237,19 @@ static int max63xx_wdt_probe(struct plat + return -EINVAL; + } + ++ wdt->gpio_wdi = devm_gpiod_get(dev, NULL, GPIOD_FLAGS_BIT_DIR_OUT); ++ if (IS_ERR(wdt->gpio_wdi) && PTR_ERR(wdt->gpio_wdi) != -ENOENT) ++ return dev_err_probe(dev, PTR_ERR(wdt->gpio_wdi), ++ "unable to request gpio: %ld\n", ++ PTR_ERR(wdt->gpio_wdi)); ++ + err = max63xx_mmap_init(pdev, wdt); + if (err) + return err; + ++ if (!IS_ERR(wdt->gpio_wdi)) ++ wdt->ping = max63xx_gpio_ping; ++ + platform_set_drvdata(pdev, &wdt->wdd); + watchdog_set_drvdata(&wdt->wdd, wdt); + diff --git a/target/linux/generic/pending-6.12/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch b/target/linux/generic/pending-6.12/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch new file mode 100644 index 00000000000..b5cc52dc028 --- /dev/null +++ b/target/linux/generic/pending-6.12/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch @@ -0,0 +1,81 @@ +From: Felix Fietkau +Subject: jffs2: use .rename2 and add RENAME_WHITEOUT support + +It is required for renames on overlayfs + +Signed-off-by: Felix Fietkau +--- + +--- a/fs/jffs2/dir.c ++++ b/fs/jffs2/dir.c +@@ -620,8 +620,8 @@ static int jffs2_rmdir (struct inode *di + return ret; + } + +-static int jffs2_mknod (struct mnt_idmap *idmap, struct inode *dir_i, +- struct dentry *dentry, umode_t mode, dev_t rdev) ++static int __jffs2_mknod (struct mnt_idmap *idmap, struct inode *dir_i, ++ struct dentry *dentry, umode_t mode, dev_t rdev, bool whiteout) + { + struct jffs2_inode_info *f, *dir_f; + struct jffs2_sb_info *c; +@@ -761,7 +761,11 @@ static int jffs2_mknod (struct mnt_idmap + mutex_unlock(&dir_f->sem); + jffs2_complete_reservation(c); + +- d_instantiate_new(dentry, inode); ++ if (!whiteout) ++ d_instantiate_new(dentry, inode); ++ else ++ unlock_new_inode(inode); ++ + return 0; + + fail: +@@ -769,6 +773,19 @@ static int jffs2_mknod (struct mnt_idmap + return ret; + } + ++static int jffs2_mknod (struct mnt_idmap *idmap, struct inode *dir_i, ++ struct dentry *dentry, umode_t mode, dev_t rdev) ++{ ++ return __jffs2_mknod(idmap, dir_i, dentry, mode, rdev, false); ++} ++ ++static int jffs2_whiteout (struct mnt_idmap *idmap, struct inode *old_dir, ++ struct dentry *old_dentry) ++{ ++ return __jffs2_mknod(idmap, old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, ++ WHITEOUT_DEV, true); ++} ++ + static int jffs2_rename (struct mnt_idmap *idmap, + struct inode *old_dir_i, struct dentry *old_dentry, + struct inode *new_dir_i, struct dentry *new_dentry, +@@ -780,7 +797,7 @@ static int jffs2_rename (struct mnt_idma + uint8_t type; + uint32_t now; + +- if (flags & ~RENAME_NOREPLACE) ++ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT)) + return -EINVAL; + + /* The VFS will check for us and prevent trying to rename a +@@ -846,9 +863,14 @@ static int jffs2_rename (struct mnt_idma + if (d_is_dir(old_dentry) && !victim_f) + inc_nlink(new_dir_i); + +- /* Unlink the original */ +- ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), +- old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); ++ if (flags & RENAME_WHITEOUT) ++ /* Replace with whiteout */ ++ ret = jffs2_whiteout(idmap, old_dir_i, old_dentry); ++ else ++ /* Unlink the original */ ++ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), ++ old_dentry->d_name.name, ++ old_dentry->d_name.len, NULL, now); + + /* We don't touch inode->i_nlink */ + diff --git a/target/linux/generic/pending-6.12/141-jffs2-add-RENAME_EXCHANGE-support.patch b/target/linux/generic/pending-6.12/141-jffs2-add-RENAME_EXCHANGE-support.patch new file mode 100644 index 00000000000..44ba3bd959c --- /dev/null +++ b/target/linux/generic/pending-6.12/141-jffs2-add-RENAME_EXCHANGE-support.patch @@ -0,0 +1,73 @@ +From: Felix Fietkau +Subject: jffs2: add RENAME_EXCHANGE support + +Signed-off-by: Felix Fietkau +--- + +--- a/fs/jffs2/dir.c ++++ b/fs/jffs2/dir.c +@@ -794,18 +794,31 @@ static int jffs2_rename (struct mnt_idma + int ret; + struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); + struct jffs2_inode_info *victim_f = NULL; ++ struct inode *fst_inode = d_inode(old_dentry); ++ struct inode *snd_inode = d_inode(new_dentry); + uint8_t type; + uint32_t now; + +- if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT)) ++ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT|RENAME_EXCHANGE)) + return -EINVAL; + ++ if ((flags & RENAME_EXCHANGE) && (old_dir_i != new_dir_i)) { ++ if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) { ++ inc_nlink(new_dir_i); ++ drop_nlink(old_dir_i); ++ } ++ else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) { ++ drop_nlink(new_dir_i); ++ inc_nlink(old_dir_i); ++ } ++ } ++ + /* The VFS will check for us and prevent trying to rename a + * file over a directory and vice versa, but if it's a directory, + * the VFS can't check whether the victim is empty. The filesystem + * needs to do that for itself. + */ +- if (d_really_is_positive(new_dentry)) { ++ if (d_really_is_positive(new_dentry) && !(flags & RENAME_EXCHANGE)) { + victim_f = JFFS2_INODE_INFO(d_inode(new_dentry)); + if (d_is_dir(new_dentry)) { + struct jffs2_full_dirent *fd; +@@ -840,7 +853,7 @@ static int jffs2_rename (struct mnt_idma + if (ret) + return ret; + +- if (victim_f) { ++ if (victim_f && !(flags & RENAME_EXCHANGE)) { + /* There was a victim. Kill it off nicely */ + if (d_is_dir(new_dentry)) + clear_nlink(d_inode(new_dentry)); +@@ -866,6 +879,12 @@ static int jffs2_rename (struct mnt_idma + if (flags & RENAME_WHITEOUT) + /* Replace with whiteout */ + ret = jffs2_whiteout(idmap, old_dir_i, old_dentry); ++ else if (flags & RENAME_EXCHANGE) ++ /* Replace the original */ ++ ret = jffs2_do_link(c, JFFS2_INODE_INFO(old_dir_i), ++ d_inode(new_dentry)->i_ino, type, ++ old_dentry->d_name.name, old_dentry->d_name.len, ++ now); + else + /* Unlink the original */ + ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), +@@ -898,7 +917,7 @@ static int jffs2_rename (struct mnt_idma + return ret; + } + +- if (d_is_dir(old_dentry)) ++ if (d_is_dir(old_dentry) && !(flags & RENAME_EXCHANGE)) + drop_nlink(old_dir_i); + + inode_set_mtime_to_ts(old_dir_i, diff --git a/target/linux/generic/pending-6.12/142-jffs2-add-splice-ops.patch b/target/linux/generic/pending-6.12/142-jffs2-add-splice-ops.patch new file mode 100644 index 00000000000..ea57158cc22 --- /dev/null +++ b/target/linux/generic/pending-6.12/142-jffs2-add-splice-ops.patch @@ -0,0 +1,20 @@ +From: Felix Fietkau +Subject: jffs2: add splice ops + +Add splice_read using generic_file_splice_read. +Add splice_write using iter_file_splice_write + +Signed-off-by: Felix Fietkau +--- + +--- a/fs/jffs2/file.c ++++ b/fs/jffs2/file.c +@@ -53,6 +53,8 @@ const struct file_operations jffs2_file_ + .open = generic_file_open, + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, ++ .splice_read = filemap_splice_read, ++ .splice_write = iter_file_splice_write, + .unlocked_ioctl=jffs2_ioctl, + .mmap = generic_file_readonly_mmap, + .fsync = jffs2_fsync, diff --git a/target/linux/generic/pending-6.12/150-bridge_allow_receiption_on_disabled_port.patch b/target/linux/generic/pending-6.12/150-bridge_allow_receiption_on_disabled_port.patch new file mode 100644 index 00000000000..530f2c3a388 --- /dev/null +++ b/target/linux/generic/pending-6.12/150-bridge_allow_receiption_on_disabled_port.patch @@ -0,0 +1,45 @@ +From: Stephen Hemminger +Subject: bridge: allow receiption on disabled port + +When an ethernet device is enslaved to a bridge, and the bridge STP +detects loss of carrier (or operational state down), then normally +packet receiption is blocked. + +This breaks control applications like WPA which maybe expecting to +receive packets to negotiate to bring link up. The bridge needs to +block forwarding packets from these disabled ports, but there is no +hard requirement to not allow local packet delivery. + +Signed-off-by: Stephen Hemminger +Signed-off-by: Felix Fietkau + +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -245,6 +245,9 @@ static void __br_handle_local_finish(str + /* note: already called with rcu_read_lock */ + static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb) + { ++ struct net_bridge_port *p = br_port_get_rcu(skb->dev); ++ ++ if (p->state != BR_STATE_DISABLED) + __br_handle_local_finish(skb); + + /* return 1 to signal the okfn() was called so it's ok to use the skb */ +@@ -416,6 +419,17 @@ forward: + goto defer_stp_filtering; + + switch (p->state) { ++ case BR_STATE_DISABLED: ++ if (ether_addr_equal(p->br->dev->dev_addr, dest)) ++ skb->pkt_type = PACKET_HOST; ++ ++ if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, ++ dev_net(skb->dev), NULL, skb, skb->dev, NULL, ++ br_handle_local_finish) == 1) { ++ return RX_HANDLER_PASS; ++ } ++ break; ++ + case BR_STATE_FORWARDING: + case BR_STATE_LEARNING: + defer_stp_filtering: diff --git a/target/linux/generic/pending-6.12/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch b/target/linux/generic/pending-6.12/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch new file mode 100644 index 00000000000..3abeacaffb8 --- /dev/null +++ b/target/linux/generic/pending-6.12/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch @@ -0,0 +1,37 @@ +From: Felix Fietkau +Date: Thu, 4 Jan 2024 15:21:21 +0100 +Subject: [PATCH] net: bridge: do not send arp replies if src and target hw + addr is the same + +There are broken devices in the wild that handle duplicate IP address +detection by sending out ARP requests for the IP that they received from a +DHCP server and refuse the address if they get a reply. +When proxyarp is enabled, they would go into a loop of requesting an address +and then NAKing it again. + +Link: https://github.com/openwrt/openwrt/issues/14309 +Signed-off-by: Felix Fietkau +--- + +--- a/net/bridge/br_arp_nd_proxy.c ++++ b/net/bridge/br_arp_nd_proxy.c +@@ -204,7 +204,10 @@ void br_do_proxy_suppress_arp(struct sk_ + if ((p && (p->flags & BR_PROXYARP)) || + (f->dst && (f->dst->flags & BR_PROXYARP_WIFI)) || + br_is_neigh_suppress_enabled(f->dst, vid)) { +- if (!vid) ++ replied = true; ++ if (!memcmp(n->ha, sha, dev->addr_len)) ++ replied = false; ++ else if (!vid) + br_arp_send(br, p, skb->dev, sip, tip, + sha, n->ha, sha, 0, 0); + else +@@ -212,7 +215,6 @@ void br_do_proxy_suppress_arp(struct sk_ + sha, n->ha, sha, + skb->vlan_proto, + skb_vlan_tag_get(skb)); +- replied = true; + } + + /* If we have replied or as long as we know the diff --git a/target/linux/generic/pending-6.12/190-rtc-rs5c372-support_alarms_up_to_1_week.patch b/target/linux/generic/pending-6.12/190-rtc-rs5c372-support_alarms_up_to_1_week.patch new file mode 100644 index 00000000000..2f5c2228c7a --- /dev/null +++ b/target/linux/generic/pending-6.12/190-rtc-rs5c372-support_alarms_up_to_1_week.patch @@ -0,0 +1,94 @@ +From: Daniel González Cabanelas +Subject: [PATCH 1/2] rtc: rs5c372: support alarms up to 1 week + +The Ricoh R2221x, R2223x, RS5C372, RV5C387A chips can handle 1 week +alarms. + +Read the "wday" alarm register and convert it to a date to support up 1 +week in our driver. + +Signed-off-by: Daniel González Cabanelas +--- + drivers/rtc/rtc-rs5c372.c | 48 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 42 insertions(+), 6 deletions(-) + +--- a/drivers/rtc/rtc-rs5c372.c ++++ b/drivers/rtc/rtc-rs5c372.c +@@ -399,7 +399,9 @@ static int rs5c_read_alarm(struct device + { + struct i2c_client *client = to_i2c_client(dev); + struct rs5c372 *rs5c = i2c_get_clientdata(client); +- int status; ++ int status, wday_offs; ++ struct rtc_time rtc; ++ unsigned long alarm_secs; + + status = rs5c_get_regs(rs5c); + if (status < 0) +@@ -409,6 +411,30 @@ static int rs5c_read_alarm(struct device + t->time.tm_sec = 0; + t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f); + t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]); ++ t->time.tm_wday = ffs(rs5c->regs[RS5C_REG_ALARM_A_WDAY] & 0x7f) - 1; ++ ++ /* determine the day, month and year based on alarm wday, taking as a ++ * reference the current time from the rtc ++ */ ++ status = rs5c372_rtc_read_time(dev, &rtc); ++ if (status < 0) ++ return status; ++ ++ wday_offs = t->time.tm_wday - rtc.tm_wday; ++ alarm_secs = mktime64(rtc.tm_year + 1900, ++ rtc.tm_mon + 1, ++ rtc.tm_mday + wday_offs, ++ t->time.tm_hour, ++ t->time.tm_min, ++ t->time.tm_sec); ++ ++ if (wday_offs < 0 || (wday_offs == 0 && ++ (t->time.tm_hour < rtc.tm_hour || ++ (t->time.tm_hour == rtc.tm_hour && ++ t->time.tm_min <= rtc.tm_min)))) ++ alarm_secs += 7 * 86400; ++ ++ rtc_time64_to_tm(alarm_secs, &t->time); + + /* ... and status */ + t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE); +@@ -423,12 +449,20 @@ static int rs5c_set_alarm(struct device + struct rs5c372 *rs5c = i2c_get_clientdata(client); + int status, addr, i; + unsigned char buf[3]; ++ struct rtc_time rtc_tm; ++ unsigned long rtc_secs, alarm_secs; + +- /* only handle up to 24 hours in the future, like RTC_ALM_SET */ +- if (t->time.tm_mday != -1 +- || t->time.tm_mon != -1 +- || t->time.tm_year != -1) ++ /* chip only can handle alarms up to one week in the future*/ ++ status = rs5c372_rtc_read_time(dev, &rtc_tm); ++ if (status) ++ return status; ++ rtc_secs = rtc_tm_to_time64(&rtc_tm); ++ alarm_secs = rtc_tm_to_time64(&t->time); ++ if (alarm_secs >= rtc_secs + 7 * 86400) { ++ dev_err(dev, "%s: alarm maximum is one week in the future (%d)\n", ++ __func__, status); + return -EINVAL; ++ } + + /* REVISIT: round up tm_sec */ + +@@ -449,7 +483,9 @@ static int rs5c_set_alarm(struct device + /* set alarm */ + buf[0] = bin2bcd(t->time.tm_min); + buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour); +- buf[2] = 0x7f; /* any/all days */ ++ /* each bit is the day of the week, 0x7f means all days */ ++ buf[2] = (t->time.tm_wday >= 0 && t->time.tm_wday < 7) ? ++ BIT(t->time.tm_wday) : 0x7f; + + for (i = 0; i < sizeof(buf); i++) { + addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i); diff --git a/target/linux/generic/pending-6.12/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch b/target/linux/generic/pending-6.12/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch new file mode 100644 index 00000000000..a29c548bbdf --- /dev/null +++ b/target/linux/generic/pending-6.12/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch @@ -0,0 +1,71 @@ +From: Daniel González Cabanelas +Subject: [PATCH 2/2] rtc: rs5c372: let the alarm to be used as wakeup source + +Currently there is no use for the interrupts on the rs5c372 RTC and the +wakealarm isn't enabled. There are some devices like NASes which use this +RTC to wake up from the power off state when the INTR pin is activated by +the alarm clock. + +Enable the alarm and let to be used as a wakeup source. + +Tested on a Buffalo LS421DE NAS. + +Signed-off-by: Daniel González Cabanelas +--- + drivers/rtc/rtc-rs5c372.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/rtc/rtc-rs5c372.c ++++ b/drivers/rtc/rtc-rs5c372.c +@@ -832,6 +832,7 @@ static int rs5c372_probe(struct i2c_clie + int err = 0; + int smbus_mode = 0; + struct rs5c372 *rs5c372; ++ bool rs5c372_can_wakeup_device = false; + + dev_dbg(&client->dev, "%s\n", __func__); + +@@ -868,6 +869,12 @@ static int rs5c372_probe(struct i2c_clie + rs5c372->type = id->driver_data; + } + ++#ifdef CONFIG_OF ++ if(of_property_read_bool(client->dev.of_node, ++ "wakeup-source")) ++ rs5c372_can_wakeup_device = true; ++#endif ++ + /* we read registers 0x0f then 0x00-0x0f; skip the first one */ + rs5c372->regs = &rs5c372->buf[1]; + rs5c372->smbus = smbus_mode; +@@ -901,6 +908,8 @@ static int rs5c372_probe(struct i2c_clie + goto exit; + } + ++ rs5c372->has_irq = 1; ++ + /* if the oscillator lost power and no other software (like + * the bootloader) set it up, do it here. + * +@@ -927,6 +936,10 @@ static int rs5c372_probe(struct i2c_clie + ); + + /* REVISIT use client->irq to register alarm irq ... */ ++ if (rs5c372_can_wakeup_device) { ++ device_init_wakeup(&client->dev, true); ++ } ++ + rs5c372->rtc = devm_rtc_device_register(&client->dev, + rs5c372_driver.driver.name, + &rs5c372_rtc_ops, THIS_MODULE); +@@ -940,6 +953,10 @@ static int rs5c372_probe(struct i2c_clie + if (err) + goto exit; + ++ /* the rs5c372 alarm only supports a minute accuracy */ ++ set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rs5c372->rtc->features); ++ clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rs5c372->rtc->features); ++ + return 0; + + exit: diff --git a/target/linux/generic/pending-6.12/200-ARM-9404-1-arm32-fix-boot-hang-with-HAVE_LD_DEAD_COD.patch b/target/linux/generic/pending-6.12/200-ARM-9404-1-arm32-fix-boot-hang-with-HAVE_LD_DEAD_COD.patch new file mode 100644 index 00000000000..f36b1182ed2 --- /dev/null +++ b/target/linux/generic/pending-6.12/200-ARM-9404-1-arm32-fix-boot-hang-with-HAVE_LD_DEAD_COD.patch @@ -0,0 +1,79 @@ +From cf3d39cfd29ab7bcbd6aa79d4a2f132817969e3d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 15 Apr 2025 23:16:40 +0200 +Subject: [PATCH] ARM: 9404/1: arm32: fix boot hang with + HAVE_LD_DEAD_CODE_DATA_ELIMINATION + +It was reported that some SoC (mvebu based for example) hang on kernel +loading. A variant of the feature was present in OpenWrt from long ago +and adding the additional entry with KEEP, fix the problem. + +Fixes: ed0f94102251 ("ARM: 9404/1: arm32: enable HAVE_LD_DEAD_CODE_DATA_ELIMINATION") +Signed-off-by: Christian Marangi +--- + arch/arm/include/asm/vmlinux.lds.h | 10 +++++----- + arch/arm/kernel/vmlinux.lds.S | 4 ++-- + 2 files changed, 7 insertions(+), 7 deletions(-) + +--- a/arch/arm/include/asm/vmlinux.lds.h ++++ b/arch/arm/include/asm/vmlinux.lds.h +@@ -54,7 +54,7 @@ + #define IDMAP_TEXT \ + ALIGN_FUNCTION(); \ + __idmap_text_start = .; \ +- *(.idmap.text) \ ++ KEEP(*(.idmap.text)) \ + __idmap_text_end = .; \ + + #define ARM_DISCARD \ +@@ -114,12 +114,12 @@ + . = ALIGN(8); \ + .ARM.unwind_idx : { \ + __start_unwind_idx = .; \ +- *(.ARM.exidx*) \ ++ KEEP(*(.ARM.exidx*)) \ + __stop_unwind_idx = .; \ + } \ + .ARM.unwind_tab : { \ + __start_unwind_tab = .; \ +- *(.ARM.extab*) \ ++ KEEP(*(.ARM.extab*)) \ + __stop_unwind_tab = .; \ + } + +@@ -131,7 +131,7 @@ + __vectors_lma = .; \ + OVERLAY 0xffff0000 : NOCROSSREFS AT(__vectors_lma) { \ + .vectors { \ +- OVERLAY_KEEP(*(.vectors)) \ ++ KEEP(*(.vectors)) \ + } \ + .vectors.bhb.loop8 { \ + OVERLAY_KEEP(*(.vectors.bhb.loop8)) \ +@@ -149,7 +149,7 @@ + \ + __stubs_lma = .; \ + .stubs ADDR(.vectors) + 0x1000 : AT(__stubs_lma) { \ +- *(.stubs) \ ++ KEEP(*(.stubs)) \ + } \ + ARM_LMA(__stubs, .stubs); \ + . = __stubs_lma + SIZEOF(.stubs); \ +--- a/arch/arm/kernel/vmlinux.lds.S ++++ b/arch/arm/kernel/vmlinux.lds.S +@@ -104,13 +104,13 @@ SECTIONS + } + .init.tagtable : { + __tagtable_begin = .; +- *(.taglist.init) ++ KEEP(*(.taglist.init)) + __tagtable_end = .; + } + #ifdef CONFIG_SMP_ON_UP + .init.smpalt : { + __smpalt_begin = .; +- *(.alt.smp.init) ++ KEEP(*(.alt.smp.init)) + __smpalt_end = .; + } + #endif diff --git a/target/linux/generic/pending-6.12/203-kallsyms_uncompressed.patch b/target/linux/generic/pending-6.12/203-kallsyms_uncompressed.patch new file mode 100644 index 00000000000..40de8f6072d --- /dev/null +++ b/target/linux/generic/pending-6.12/203-kallsyms_uncompressed.patch @@ -0,0 +1,183 @@ +From: Felix Fietkau +Subject: kernel: add a config option for keeping the kallsyms table uncompressed, saving ~9kb kernel size after lzma on ar71xx + +[john@phrozen.org: added to my upstream queue 30.12.2016] +lede-commit: e0e3509b5ce2ccf93d4d67ea907613f5f7ec2eed +Signed-off-by: Felix Fietkau +--- + init/Kconfig | 11 +++++++++++ + kernel/kallsyms.c | 8 ++++++++ + kernel/vmcore_info.c | 2 ++ + scripts/kallsyms.c | 12 ++++++++++++ + scripts/link-vmlinux.sh | 4 ++++ + 5 files changed, 37 insertions(+) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1534,6 +1534,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW + the unaligned access emulation. + see arch/parisc/kernel/unaligned.c for reference + ++config KALLSYMS_UNCOMPRESSED ++ bool "Keep kallsyms uncompressed" ++ depends on KALLSYMS ++ help ++ Normally kallsyms contains compressed symbols (using a token table), ++ reducing the uncompressed kernel image size. Keeping the symbol table ++ uncompressed significantly improves the size of this part in compressed ++ kernel images. ++ ++ Say N unless you need compressed kernel images to be small. ++ + config HAVE_PCSPKR_PLATFORM + bool + +--- a/kernel/kallsyms.c ++++ b/kernel/kallsyms.c +@@ -69,6 +69,11 @@ static unsigned int kallsyms_expand_symb + * For every byte on the compressed symbol data, copy the table + * entry for that byte. + */ ++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED ++ memcpy(result, data + 1, len - 1); ++ result += len - 1; ++ len = 0; ++#endif + while (len) { + tptr = &kallsyms_token_table[kallsyms_token_index[*data]]; + data++; +@@ -101,6 +106,9 @@ tail: + */ + static char kallsyms_get_symbol_type(unsigned int off) + { ++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED ++ return kallsyms_names[off + 1]; ++#endif + /* + * Get just the first code, look it up in the token table, + * and return the first char from this token. If MSB of length +--- a/kernel/vmcore_info.c ++++ b/kernel/vmcore_info.c +@@ -214,8 +214,10 @@ static int __init crash_save_vmcoreinfo_ + #ifdef CONFIG_KALLSYMS + VMCOREINFO_SYMBOL(kallsyms_names); + VMCOREINFO_SYMBOL(kallsyms_num_syms); ++#ifndef CONFIG_KALLSYMS_UNCOMPRESSED + VMCOREINFO_SYMBOL(kallsyms_token_table); + VMCOREINFO_SYMBOL(kallsyms_token_index); ++#endif + VMCOREINFO_SYMBOL(kallsyms_offsets); + VMCOREINFO_SYMBOL(kallsyms_relative_base); + #endif /* CONFIG_KALLSYMS */ +--- a/scripts/kallsyms.c ++++ b/scripts/kallsyms.c +@@ -62,6 +62,7 @@ static struct addr_range percpu_range = + static struct sym_entry **table; + static unsigned int table_size, table_cnt; + static int all_symbols; ++static int uncompressed; + static int absolute_percpu; + + static int token_profit[0x10000]; +@@ -412,13 +413,17 @@ static void write_src(void) + for (k = 0; k < table[i]->len; k++) + printf(", 0x%02x", table[i]->sym[k]); + +- /* +- * Now that we wrote out the compressed symbol name, restore the +- * original name and print it in the comment. +- */ +- expand_symbol(table[i]->sym, table[i]->len, buf); +- strcpy((char *)table[i]->sym, buf); +- printf("\t/* %s */\n", table[i]->sym); ++ if (!uncompressed) { ++ /* ++ * Now that we wrote out the compressed symbol name, restore the ++ * original name and print it in the comment. ++ */ ++ expand_symbol(table[i]->sym, table[i]->len, buf); ++ strcpy((char *)table[i]->sym, buf); ++ printf("\t/* %s */\n", table[i]->sym); ++ } else { ++ printf("\n"); ++ } + } + printf("\n"); + +@@ -429,20 +434,22 @@ static void write_src(void) + + free(markers); + +- output_label("kallsyms_token_table"); +- off = 0; +- for (i = 0; i < 256; i++) { +- best_idx[i] = off; +- expand_symbol(best_table[i], best_table_len[i], buf); +- printf("\t.asciz\t\"%s\"\n", buf); +- off += strlen(buf) + 1; ++ if (!uncompressed) { ++ output_label("kallsyms_token_table"); ++ off = 0; ++ for (i = 0; i < 256; i++) { ++ best_idx[i] = off; ++ expand_symbol(best_table[i], best_table_len[i], buf); ++ printf("\t.asciz\t\"%s\"\n", buf); ++ off += strlen(buf) + 1; ++ } ++ printf("\n"); ++ ++ output_label("kallsyms_token_index"); ++ for (i = 0; i < 256; i++) ++ printf("\t.short\t%d\n", best_idx[i]); ++ printf("\n"); + } +- printf("\n"); +- +- output_label("kallsyms_token_index"); +- for (i = 0; i < 256; i++) +- printf("\t.short\t%d\n", best_idx[i]); +- printf("\n"); + + output_label("kallsyms_offsets"); + +@@ -532,6 +539,9 @@ static unsigned char *find_token(unsigne + { + int i; + ++ if (uncompressed) ++ return NULL; ++ + for (i = 0; i < len - 1; i++) { + if (str[i] == token[0] && str[i+1] == token[1]) + return &str[i]; +@@ -604,6 +614,9 @@ static void optimize_result(void) + { + int i, best; + ++ if (uncompressed) ++ return; ++ + /* using the '\0' symbol last allows compress_symbols to use standard + * fast string functions */ + for (i = 255; i >= 0; i--) { +@@ -763,6 +776,7 @@ int main(int argc, char **argv) + static const struct option long_options[] = { + {"all-symbols", no_argument, &all_symbols, 1}, + {"absolute-percpu", no_argument, &absolute_percpu, 1}, ++ {"uncompressed", no_argument, &uncompressed, 1}, + {}, + }; + +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -144,6 +144,10 @@ kallsyms() + kallsymopt="${kallsymopt} --absolute-percpu" + fi + ++ if is_enabled CONFIG_KALLSYMS_UNCOMPRESSED; then ++ kallsymopt="${kallsymopt} --uncompressed" ++ fi ++ + info KSYMS "${2}.S" + scripts/kallsyms ${kallsymopt} "${1}" > "${2}.S" + diff --git a/target/linux/generic/pending-6.12/205-backtrace_module_info.patch b/target/linux/generic/pending-6.12/205-backtrace_module_info.patch new file mode 100644 index 00000000000..de9f8602eef --- /dev/null +++ b/target/linux/generic/pending-6.12/205-backtrace_module_info.patch @@ -0,0 +1,48 @@ +From: Felix Fietkau +Subject: kernel: when KALLSYMS is disabled, print module address + size for matching backtrace entries + +[john@phrozen.org: felix will add this to his upstream queue] + +dmitrmax@gmail.com: fixed FTB when CONFIG_MODULES=n + +lede-commit 53827cdc824556cda910b23ce5030c363b8f1461 +Signed-off-by: Felix Fietkau +Signed-off-by: Maksim Dmitrichenko +--- + lib/vsprintf.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +--- a/lib/vsprintf.c ++++ b/lib/vsprintf.c +@@ -983,8 +983,12 @@ char *symbol_string(char *buf, char *end + struct printf_spec spec, const char *fmt) + { + unsigned long value; +-#ifdef CONFIG_KALLSYMS + char sym[KSYM_SYMBOL_LEN]; ++#ifndef CONFIG_KALLSYMS ++#ifdef CONFIG_MODULES ++ struct module *mod; ++#endif ++ int len; + #endif + + if (fmt[1] == 'R') +@@ -1005,8 +1009,16 @@ char *symbol_string(char *buf, char *end + + return string_nocheck(buf, end, sym, spec); + #else +- return special_hex_number(buf, end, value, sizeof(void *)); ++ len = snprintf(sym, sizeof(sym), "0x%lx", value); ++#ifdef CONFIG_MODULES ++ mod = __module_address(value); ++ if (mod) ++ snprintf(sym + len, sizeof(sym) - len, " [%s@%p+0x%x]", ++ mod->name, mod->mem[MOD_TEXT].base, ++ mod->mem[MOD_TEXT].size); ++#endif + #endif ++ return string(buf, end, sym, spec); + } + + static const struct printf_spec default_str_spec = { diff --git a/target/linux/generic/pending-6.12/240-remove-unsane-filenames-from-deps_initramfs-list.patch b/target/linux/generic/pending-6.12/240-remove-unsane-filenames-from-deps_initramfs-list.patch new file mode 100644 index 00000000000..9e78284ecf7 --- /dev/null +++ b/target/linux/generic/pending-6.12/240-remove-unsane-filenames-from-deps_initramfs-list.patch @@ -0,0 +1,30 @@ +From: Gabor Juhos +Subject: usr: sanitize deps_initramfs list + +If any filename in the intramfs dependency +list contains a colon, that causes a kernel +build error like this: + +/devel/openwrt/build_dir/linux-ar71xx_generic/linux-3.6.6/usr/Makefile:58: *** multiple target patterns. Stop. +make[5]: *** [usr] Error 2 + +Fix it by removing such filenames from the +deps_initramfs list. + +Signed-off-by: Gabor Juhos +Signed-off-by: Felix Fietkau +--- + usr/Makefile | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/usr/Makefile ++++ b/usr/Makefile +@@ -56,6 +56,8 @@ hostprogs := gen_init_cpio + # The dependency list is generated by gen_initramfs.sh -l + -include $(obj)/.initramfs_data.cpio.d + ++deps_initramfs := $(foreach v,$(deps_initramfs),$(if $(findstring :,$(v)),,$(v))) ++ + # do not try to update files included in initramfs + $(deps_initramfs): ; + diff --git a/target/linux/generic/pending-6.12/250-kernel-fork-Increase-minimum-number-of-allowed-threa.patch b/target/linux/generic/pending-6.12/250-kernel-fork-Increase-minimum-number-of-allowed-threa.patch new file mode 100644 index 00000000000..5255a9cd762 --- /dev/null +++ b/target/linux/generic/pending-6.12/250-kernel-fork-Increase-minimum-number-of-allowed-threa.patch @@ -0,0 +1,37 @@ +From a22c4c7bbfad3e93e717e16f6faf343259add27a Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Fri, 27 Jun 2025 11:09:04 +0100 +Subject: [PATCH v2] kernel/fork: Increase minimum number of allowed threads + +A modern Linux system creates much more than 20 threads at bootup. +When I booted up OpenWrt in qemu the system sometimes failed to boot up +when it wanted to create the 419th thread. The VM had 128MB RAM and the +calculation in set_max_threads() calculated that max_threads should be +set to 419. When the system booted up it tried to notify the user space +about every device it created because CONFIG_UEVENT_HELPER was set and +used. I counted 1299 calls to call_usermodehelper_setup(), all of +them try to create a new thread and call the userspace hotplug script in +it. + +This fixes bootup of Linux on systems with low memory. + +I saw the problem with qemu 10.0.2 using these commands: +qemu-system-aarch64 -machine virt -cpu cortex-a57 -nographic + +Cc: stable@vger.kernel.org +Signed-off-by: Hauke Mehrtens +--- + kernel/fork.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -123,7 +123,7 @@ + /* + * Minimum number of threads to boot the kernel + */ +-#define MIN_THREADS 20 ++#define MIN_THREADS 600 + + /* + * Maximum number of threads diff --git a/target/linux/generic/pending-6.12/270-platform-mikrotik-build-bits.patch b/target/linux/generic/pending-6.12/270-platform-mikrotik-build-bits.patch new file mode 100644 index 00000000000..772d9feeb8b --- /dev/null +++ b/target/linux/generic/pending-6.12/270-platform-mikrotik-build-bits.patch @@ -0,0 +1,31 @@ +From c2deb5ef01a0ef09088832744cbace9e239a6ee0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= +Date: Sat, 28 Mar 2020 12:11:50 +0100 +Subject: [PATCH] generic: platform/mikrotik build bits (5.4) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch adds platform/mikrotik kernel build bits + +Signed-off-by: Thibaut VARÈNE +--- + drivers/platform/Kconfig | 2 ++ + drivers/platform/Makefile | 1 + + 2 files changed, 3 insertions(+) + +--- a/drivers/platform/Kconfig ++++ b/drivers/platform/Kconfig +@@ -18,3 +18,5 @@ source "drivers/platform/surface/Kconfig + source "drivers/platform/x86/Kconfig" + + source "drivers/platform/arm64/Kconfig" ++ ++source "drivers/platform/mikrotik/Kconfig" +--- a/drivers/platform/Makefile ++++ b/drivers/platform/Makefile +@@ -13,3 +13,4 @@ obj-$(CONFIG_CHROME_PLATFORMS) += chrome + obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/ + obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ + obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += arm64/ ++obj-$(CONFIG_MIKROTIK) += mikrotik/ diff --git a/target/linux/generic/pending-6.12/300-mips_expose_boot_raw.patch b/target/linux/generic/pending-6.12/300-mips_expose_boot_raw.patch new file mode 100644 index 00000000000..ca8d0103465 --- /dev/null +++ b/target/linux/generic/pending-6.12/300-mips_expose_boot_raw.patch @@ -0,0 +1,40 @@ +From: Mark Miller +Subject: mips: expose CONFIG_BOOT_RAW + +This exposes the CONFIG_BOOT_RAW symbol in Kconfig. This is needed on +certain Broadcom chipsets running CFE in order to load the kernel. + +Signed-off-by: Mark Miller +Acked-by: Rob Landley +--- +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -1055,9 +1055,6 @@ config FW_ARC + config ARCH_MAY_HAVE_PC_FDC + bool + +-config BOOT_RAW +- bool +- + config CEVT_BCM1480 + bool + +@@ -2990,6 +2987,18 @@ choice + bool "Extend builtin kernel arguments with bootloader arguments" + endchoice + ++config BOOT_RAW ++ bool "Enable the kernel to be executed from the load address" ++ default n ++ help ++ Allow the kernel to be executed from the load address for ++ bootloaders which cannot read the ELF format. This places ++ a jump to start_kernel at the load address. ++ ++ If unsure, say N. ++ ++ ++ + endmenu + + config LOCKDEP_SUPPORT diff --git a/target/linux/generic/pending-6.12/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch b/target/linux/generic/pending-6.12/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch new file mode 100644 index 00000000000..b3cb5f0cde0 --- /dev/null +++ b/target/linux/generic/pending-6.12/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch @@ -0,0 +1,71 @@ +From e6e6ef4275978823ec3a84133fc91f4ffbef5c84 Mon Sep 17 00:00:00 2001 +From: Paul Burton +Date: Mon, 22 Feb 2016 18:09:44 +0000 +Subject: [PATCH] MIPS: Add barriers between dcache & icache flushes + +Index-based cache operations may be arbitrarily reordered by out of +order CPUs. Thus code which writes back the dcache & then invalidates +the icache using indexed cache ops must include a barrier between +operating on the 2 caches in order to prevent the scenario in which: + + - icache invalidation occurs. + + - icache fetch occurs, due to speculation. + + - dcache writeback occurs. + +If the above were allowed to happen then the icache would contain stale +data. Forcing the dcache writeback to complete before the icache +invalidation avoids this. + +Signed-off-by: Paul Burton +Cc: James Hogan +--- + arch/mips/mm/c-r4k.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +--- a/arch/mips/mm/c-r4k.c ++++ b/arch/mips/mm/c-r4k.c +@@ -403,6 +403,7 @@ static inline void local_r4k___flush_cac + + default: + r4k_blast_dcache(); ++ mb(); /* cache instructions may be reordered */ + r4k_blast_icache(); + break; + } +@@ -483,8 +484,10 @@ static inline void local_r4k_flush_cache + if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) + r4k_blast_dcache(); + /* If executable, blast stale lines from icache */ +- if (exec) ++ if (exec) { ++ mb(); /* cache instructions may be reordered */ + r4k_blast_icache(); ++ } + } + + static void r4k_flush_cache_range(struct vm_area_struct *vma, +@@ -586,8 +589,13 @@ static inline void local_r4k_flush_cache + if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) { + vaddr ? r4k_blast_dcache_page(addr) : + r4k_blast_dcache_user_page(addr); +- if (exec && !cpu_icache_snoops_remote_store) ++ if (exec) ++ mb(); /* cache instructions may be reordered */ ++ ++ if (exec && !cpu_icache_snoops_remote_store) { + r4k_blast_scache_page(addr); ++ mb(); /* cache instructions may be reordered */ ++ } + } + if (exec) { + if (vaddr && cpu_has_vtag_icache && mm == current->active_mm) { +@@ -654,6 +662,7 @@ static inline void __local_r4k_flush_ica + else + blast_dcache_range(start, end); + } ++ mb(); /* cache instructions may be reordered */ + } + + if (type == R4K_INDEX || diff --git a/target/linux/generic/pending-6.12/302-mips_no_branch_likely.patch b/target/linux/generic/pending-6.12/302-mips_no_branch_likely.patch new file mode 100644 index 00000000000..669aa8143ac --- /dev/null +++ b/target/linux/generic/pending-6.12/302-mips_no_branch_likely.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +Subject: mips: use -mno-branch-likely for kernel and userspace + +saves ~11k kernel size after lzma and ~12k squashfs size in the + +lede-commit: 41a039f46450ffae9483d6216422098669da2900 +Signed-off-by: Felix Fietkau +--- + arch/mips/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -94,7 +94,7 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin + # machines may also. Since BFD is incredibly buggy with respect to + # crossformat linking we rely on the elf2ecoff tool for format conversion. + # +-cflags-y += -G 0 -mno-abicalls -fno-pic -pipe ++cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely + cflags-y += -msoft-float -Wa,-msoft-float + LDFLAGS_vmlinux += -G 0 -static -n -nostdlib + KBUILD_AFLAGS_MODULE += -mlong-calls diff --git a/target/linux/generic/pending-6.12/303-Revert-powerpc-dts-mpc85xx-remove-simple-bus-compatible-from-ifc-node.patch b/target/linux/generic/pending-6.12/303-Revert-powerpc-dts-mpc85xx-remove-simple-bus-compatible-from-ifc-node.patch new file mode 100644 index 00000000000..31f15a576ff --- /dev/null +++ b/target/linux/generic/pending-6.12/303-Revert-powerpc-dts-mpc85xx-remove-simple-bus-compatible-from-ifc-node.patch @@ -0,0 +1,125 @@ +From patchwork Wed Nov 5 20:55:24 2025 +From: Rosen Penev +Subject: [PATCH] Revert "powerpc: dts: mpc85xx: remove "simple-bus" compatible + from ifc node" +Date: Wed, 5 Nov 2025 12:55:24 -0800 + +This reverts commit 0bf51cc9e9e57a751b4c5dacbfa499ba5cd8bd72. + +simple-bus is needed for legacy platforms such as P1010 so that nodes +are populated properly. + +Fixes fsl,ifc-nand probing under at least P1010. + +Signed-off-by: Rosen Penev +--- + arch/powerpc/boot/dts/fsl/b4si-post.dtsi | 2 +- + arch/powerpc/boot/dts/fsl/bsc9131si-post.dtsi | 2 +- + arch/powerpc/boot/dts/fsl/bsc9132si-post.dtsi | 2 +- + arch/powerpc/boot/dts/fsl/c293si-post.dtsi | 2 +- + arch/powerpc/boot/dts/fsl/p1010si-post.dtsi | 2 +- + arch/powerpc/boot/dts/fsl/t1023si-post.dtsi | 2 +- + arch/powerpc/boot/dts/fsl/t1040si-post.dtsi | 2 +- + arch/powerpc/boot/dts/fsl/t2081si-post.dtsi | 2 +- + arch/powerpc/boot/dts/fsl/t4240si-post.dtsi | 2 +- + 9 files changed, 9 insertions(+), 9 deletions(-) + +--- a/arch/powerpc/boot/dts/fsl/b4si-post.dtsi ++++ b/arch/powerpc/boot/dts/fsl/b4si-post.dtsi +@@ -50,7 +50,7 @@ + &ifc { + #address-cells = <2>; + #size-cells = <1>; +- compatible = "fsl,ifc"; ++ compatible = "fsl,ifc", "simple-bus"; + interrupts = <25 2 0 0>; + }; + +--- a/arch/powerpc/boot/dts/fsl/bsc9131si-post.dtsi ++++ b/arch/powerpc/boot/dts/fsl/bsc9131si-post.dtsi +@@ -35,7 +35,7 @@ + &ifc { + #address-cells = <2>; + #size-cells = <1>; +- compatible = "fsl,ifc"; ++ compatible = "fsl,ifc", "simple-bus"; + interrupts = <16 2 0 0 20 2 0 0>; + }; + +--- a/arch/powerpc/boot/dts/fsl/bsc9132si-post.dtsi ++++ b/arch/powerpc/boot/dts/fsl/bsc9132si-post.dtsi +@@ -35,7 +35,7 @@ + &ifc { + #address-cells = <2>; + #size-cells = <1>; +- compatible = "fsl,ifc"; ++ compatible = "fsl,ifc", "simple-bus"; + /* FIXME: Test whether interrupts are split */ + interrupts = <16 2 0 0 20 2 0 0>; + }; +--- a/arch/powerpc/boot/dts/fsl/c293si-post.dtsi ++++ b/arch/powerpc/boot/dts/fsl/c293si-post.dtsi +@@ -35,7 +35,7 @@ + &ifc { + #address-cells = <2>; + #size-cells = <1>; +- compatible = "fsl,ifc"; ++ compatible = "fsl,ifc", "simple-bus"; + interrupts = <19 2 0 0>; + }; + +--- a/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi ++++ b/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi +@@ -35,7 +35,7 @@ + &ifc { + #address-cells = <2>; + #size-cells = <1>; +- compatible = "fsl,ifc"; ++ compatible = "fsl,ifc", "simple-bus"; + interrupts = <16 2 0 0 19 2 0 0>; + }; + +--- a/arch/powerpc/boot/dts/fsl/t1023si-post.dtsi ++++ b/arch/powerpc/boot/dts/fsl/t1023si-post.dtsi +@@ -52,7 +52,7 @@ + &ifc { + #address-cells = <2>; + #size-cells = <1>; +- compatible = "fsl,ifc"; ++ compatible = "fsl,ifc", "simple-bus"; + interrupts = <25 2 0 0>; + }; + +--- a/arch/powerpc/boot/dts/fsl/t1040si-post.dtsi ++++ b/arch/powerpc/boot/dts/fsl/t1040si-post.dtsi +@@ -52,7 +52,7 @@ + &ifc { + #address-cells = <2>; + #size-cells = <1>; +- compatible = "fsl,ifc"; ++ compatible = "fsl,ifc", "simple-bus"; + interrupts = <25 2 0 0>; + }; + +--- a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi ++++ b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi +@@ -50,7 +50,7 @@ + &ifc { + #address-cells = <2>; + #size-cells = <1>; +- compatible = "fsl,ifc"; ++ compatible = "fsl,ifc", "simple-bus"; + interrupts = <25 2 0 0>; + }; + +--- a/arch/powerpc/boot/dts/fsl/t4240si-post.dtsi ++++ b/arch/powerpc/boot/dts/fsl/t4240si-post.dtsi +@@ -50,7 +50,7 @@ + &ifc { + #address-cells = <2>; + #size-cells = <1>; +- compatible = "fsl,ifc"; ++ compatible = "fsl,ifc", "simple-bus"; + interrupts = <25 2 0 0>; + }; + diff --git a/target/linux/generic/pending-6.12/308-mips32r2_tune.patch b/target/linux/generic/pending-6.12/308-mips32r2_tune.patch new file mode 100644 index 00000000000..b9bd6104a42 --- /dev/null +++ b/target/linux/generic/pending-6.12/308-mips32r2_tune.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +Subject: kernel: add -mtune=34kc to MIPS CFLAGS when building for mips32r2 + +This provides a good tradeoff across at least 24Kc-74Kc, while also +producing smaller code. + +Signed-off-by: Felix Fietkau +--- + arch/mips/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -153,7 +153,7 @@ cflags-$(CONFIG_CPU_R4300) += $(call cc- + cflags-$(CONFIG_CPU_R4X00) += $(call cc-option,-march=r4600,-march=mips3) -Wa,--trap + cflags-$(CONFIG_CPU_TX49XX) += $(call cc-option,-march=r4600,-march=mips3) -Wa,--trap + cflags-$(CONFIG_CPU_MIPS32_R1) += -march=mips32 -Wa,--trap +-cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -Wa,--trap ++cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -mtune=34kc -Wa,--trap + cflags-$(CONFIG_CPU_MIPS32_R5) += -march=mips32r5 -Wa,--trap -modd-spreg + cflags-$(CONFIG_CPU_MIPS32_R6) += -march=mips32r6 -Wa,--trap -modd-spreg + cflags-$(CONFIG_CPU_MIPS64_R1) += -march=mips64 -Wa,--trap diff --git a/target/linux/generic/pending-6.12/310-arm_module_unresolved_weak_sym.patch b/target/linux/generic/pending-6.12/310-arm_module_unresolved_weak_sym.patch new file mode 100644 index 00000000000..c654f6baf6d --- /dev/null +++ b/target/linux/generic/pending-6.12/310-arm_module_unresolved_weak_sym.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +Subject: fix errors in unresolved weak symbols on arm + +lede-commit: 570699d4838a907c3ef9f2819bf19eb72997b32f +Signed-off-by: Felix Fietkau +--- + arch/arm/kernel/module.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/arm/kernel/module.c ++++ b/arch/arm/kernel/module.c +@@ -112,6 +112,10 @@ apply_relocate(Elf32_Shdr *sechdrs, cons + return -ENOEXEC; + } + ++ if ((IS_ERR_VALUE(sym->st_value) || !sym->st_value) && ++ ELF_ST_BIND(sym->st_info) == STB_WEAK) ++ continue; ++ + loc = dstsec->sh_addr + rel->r_offset; + + switch (ELF32_R_TYPE(rel->r_info)) { diff --git a/target/linux/generic/pending-6.12/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch b/target/linux/generic/pending-6.12/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch new file mode 100644 index 00000000000..a8c6b7a5da3 --- /dev/null +++ b/target/linux/generic/pending-6.12/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch @@ -0,0 +1,282 @@ +From: Yousong Zhou +Subject: MIPS: kexec: Accept command line parameters from userspace. + +Signed-off-by: Yousong Zhou +--- + arch/mips/kernel/machine_kexec.c | 153 +++++++++++++++++++++++++++++++----- + arch/mips/kernel/machine_kexec.h | 20 +++++ + arch/mips/kernel/relocate_kernel.S | 21 +++-- + 3 files changed, 167 insertions(+), 27 deletions(-) + create mode 100644 arch/mips/kernel/machine_kexec.h + +--- a/arch/mips/kernel/machine_kexec.c ++++ b/arch/mips/kernel/machine_kexec.c +@@ -10,14 +10,11 @@ + #include + #include + ++#include + #include + #include +- +-extern const unsigned char relocate_new_kernel[]; +-extern const size_t relocate_new_kernel_size; +- +-extern unsigned long kexec_start_address; +-extern unsigned long kexec_indirection_page; ++#include ++#include "machine_kexec.h" + + static unsigned long reboot_code_buffer; + +@@ -31,6 +28,101 @@ void (*_crash_smp_send_stop)(void) = NUL + void (*_machine_kexec_shutdown)(void) = NULL; + void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL; + ++static void machine_kexec_print_args(void) ++{ ++ unsigned long argc = (int)kexec_args[0]; ++ int i; ++ ++ pr_info("kexec_args[0] (argc): %lu\n", argc); ++ pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]); ++ pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]); ++ pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]); ++ ++ for (i = 0; i < argc; i++) { ++ pr_info("kexec_argv[%d] = %p, %s\n", ++ i, kexec_argv[i], kexec_argv[i]); ++ } ++} ++ ++static void machine_kexec_init_argv(struct kimage *image) ++{ ++ void __user *buf = NULL; ++ size_t bufsz; ++ size_t size; ++ int i; ++ ++ bufsz = 0; ++ for (i = 0; i < image->nr_segments; i++) { ++ struct kexec_segment *seg; ++ ++ seg = &image->segment[i]; ++ if (seg->bufsz < 6) ++ continue; ++ ++ if (strncmp((char *) seg->buf, "kexec ", 6)) ++ continue; ++ ++ buf = seg->buf; ++ bufsz = seg->bufsz; ++ break; ++ } ++ ++ if (!buf) ++ return; ++ ++ size = KEXEC_COMMAND_LINE_SIZE; ++ size = min(size, bufsz); ++ if (size < bufsz) ++ pr_warn("kexec command line truncated to %zd bytes\n", size); ++ ++ /* Copy to kernel space */ ++ if (copy_from_user(kexec_argv_buf, buf, size)) ++ pr_warn("kexec command line copy to kernel space failed\n"); ++ ++ kexec_argv_buf[size - 1] = 0; ++} ++ ++static void machine_kexec_parse_argv(struct kimage *image) ++{ ++ char *reboot_code_buffer; ++ int reloc_delta; ++ char *ptr; ++ int argc; ++ int i; ++ ++ ptr = kexec_argv_buf; ++ argc = 0; ++ ++ /* ++ * convert command line string to array of parameters ++ * (as bootloader does). ++ */ ++ while (ptr && *ptr && (KEXEC_MAX_ARGC > argc)) { ++ if (*ptr == ' ') { ++ *ptr++ = '\0'; ++ continue; ++ } ++ ++ kexec_argv[argc++] = ptr; ++ ptr = strchr(ptr, ' '); ++ } ++ ++ if (!argc) ++ return; ++ ++ kexec_args[0] = argc; ++ kexec_args[1] = (unsigned long)kexec_argv; ++ kexec_args[2] = 0; ++ kexec_args[3] = 0; ++ ++ reboot_code_buffer = page_address(image->control_code_page); ++ reloc_delta = reboot_code_buffer - (char *)kexec_relocate_new_kernel; ++ ++ kexec_args[1] += reloc_delta; ++ for (i = 0; i < argc; i++) ++ kexec_argv[i] += reloc_delta; ++} ++ + static void kexec_image_info(const struct kimage *kimage) + { + unsigned long i; +@@ -100,6 +192,18 @@ machine_kexec_prepare(struct kimage *kim + #endif + + kexec_image_info(kimage); ++ /* ++ * Whenever arguments passed from kexec-tools, Init the arguments as ++ * the original ones to try avoiding booting failure. ++ */ ++ ++ kexec_args[0] = fw_arg0; ++ kexec_args[1] = fw_arg1; ++ kexec_args[2] = fw_arg2; ++ kexec_args[3] = fw_arg3; ++ ++ machine_kexec_init_argv(kimage); ++ machine_kexec_parse_argv(kimage); + + if (_machine_kexec_prepare) + return _machine_kexec_prepare(kimage); +@@ -162,7 +266,7 @@ machine_crash_shutdown(struct pt_regs *r + void kexec_nonboot_cpu_jump(void) + { + local_flush_icache_range((unsigned long)relocated_kexec_smp_wait, +- reboot_code_buffer + relocate_new_kernel_size); ++ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE); + + relocated_kexec_smp_wait(NULL); + } +@@ -200,7 +304,7 @@ void kexec_reboot(void) + * machine_kexec() CPU. + */ + local_flush_icache_range(reboot_code_buffer, +- reboot_code_buffer + relocate_new_kernel_size); ++ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE); + + do_kexec = (void *)reboot_code_buffer; + do_kexec(); +@@ -213,10 +317,12 @@ machine_kexec(struct kimage *image) + unsigned long *ptr; + + reboot_code_buffer = +- (unsigned long)page_address(image->control_code_page); ++ (unsigned long)page_address(image->control_code_page); ++ pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer); + + kexec_start_address = + (unsigned long) phys_to_virt(image->start); ++ pr_info("kexec_start_address = %p\n", (void *)kexec_start_address); + + if (image->type == KEXEC_TYPE_DEFAULT) { + kexec_indirection_page = +@@ -224,9 +330,19 @@ machine_kexec(struct kimage *image) + } else { + kexec_indirection_page = (unsigned long)&image->head; + } ++ pr_info("kexec_indirection_page = %p\n", (void *)kexec_indirection_page); + +- memcpy((void*)reboot_code_buffer, relocate_new_kernel, +- relocate_new_kernel_size); ++ pr_info("Where is memcpy: %p\n", memcpy); ++ pr_info("kexec_relocate_new_kernel = %p, kexec_relocate_new_kernel_end = %p\n", ++ (void *)kexec_relocate_new_kernel, &kexec_relocate_new_kernel_end); ++ pr_info("Copy %lu bytes from %p to %p\n", KEXEC_RELOCATE_NEW_KERNEL_SIZE, ++ (void *)kexec_relocate_new_kernel, (void *)reboot_code_buffer); ++ memcpy((void*)reboot_code_buffer, kexec_relocate_new_kernel, ++ KEXEC_RELOCATE_NEW_KERNEL_SIZE); ++ ++ pr_info("Before _print_args().\n"); ++ machine_kexec_print_args(); ++ pr_info("Before eval loop.\n"); + + /* + * The generic kexec code builds a page list with physical +@@ -257,7 +373,7 @@ machine_kexec(struct kimage *image) + #ifdef CONFIG_SMP + /* All secondary cpus now may jump to kexec_wait cycle */ + relocated_kexec_smp_wait = reboot_code_buffer + +- (void *)(kexec_smp_wait - relocate_new_kernel); ++ (void *)(kexec_smp_wait - kexec_relocate_new_kernel); + smp_wmb(); + atomic_set(&kexec_ready_to_reboot, 1); + #endif +--- /dev/null ++++ b/arch/mips/kernel/machine_kexec.h +@@ -0,0 +1,20 @@ ++#ifndef _MACHINE_KEXEC_H ++#define _MACHINE_KEXEC_H ++ ++#ifndef __ASSEMBLY__ ++extern const unsigned char kexec_relocate_new_kernel[]; ++extern unsigned long kexec_relocate_new_kernel_end; ++extern unsigned long kexec_start_address; ++extern unsigned long kexec_indirection_page; ++ ++extern char kexec_argv_buf[]; ++extern char *kexec_argv[]; ++ ++#define KEXEC_RELOCATE_NEW_KERNEL_SIZE ((unsigned long)&kexec_relocate_new_kernel_end - (unsigned long)kexec_relocate_new_kernel) ++#endif /* !__ASSEMBLY__ */ ++ ++#define KEXEC_COMMAND_LINE_SIZE 256 ++#define KEXEC_ARGV_SIZE (KEXEC_COMMAND_LINE_SIZE / 16) ++#define KEXEC_MAX_ARGC (KEXEC_ARGV_SIZE / sizeof(long)) ++ ++#endif +--- a/arch/mips/kernel/relocate_kernel.S ++++ b/arch/mips/kernel/relocate_kernel.S +@@ -10,10 +10,11 @@ + #include + #include + #include ++#include "machine_kexec.h" + + #include + +-LEAF(relocate_new_kernel) ++LEAF(kexec_relocate_new_kernel) + PTR_L a0, arg0 + PTR_L a1, arg1 + PTR_L a2, arg2 +@@ -97,7 +98,7 @@ done: + #endif + /* jump to kexec_start_address */ + j s1 +- END(relocate_new_kernel) ++ END(kexec_relocate_new_kernel) + + #ifdef CONFIG_SMP + /* +@@ -176,8 +177,15 @@ EXPORT(kexec_indirection_page) + PTR_WD 0 + .size kexec_indirection_page, PTRSIZE + +-relocate_new_kernel_end: ++kexec_argv_buf: ++ EXPORT(kexec_argv_buf) ++ .skip KEXEC_COMMAND_LINE_SIZE ++ .size kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE ++ ++kexec_argv: ++ EXPORT(kexec_argv) ++ .skip KEXEC_ARGV_SIZE ++ .size kexec_argv, KEXEC_ARGV_SIZE + +-EXPORT(relocate_new_kernel_size) +- PTR_WD relocate_new_kernel_end - relocate_new_kernel +- .size relocate_new_kernel_size, PTRSIZE ++kexec_relocate_new_kernel_end: ++ EXPORT(kexec_relocate_new_kernel_end) diff --git a/target/linux/generic/pending-6.12/332-arc-add-OWRTDTB-section.patch b/target/linux/generic/pending-6.12/332-arc-add-OWRTDTB-section.patch new file mode 100644 index 00000000000..5b943f37342 --- /dev/null +++ b/target/linux/generic/pending-6.12/332-arc-add-OWRTDTB-section.patch @@ -0,0 +1,84 @@ +From bb0c3b0175240bf152fd7c644821a0cf9f77c37c Mon Sep 17 00:00:00 2001 +From: Evgeniy Didin +Date: Fri, 15 Mar 2019 18:53:38 +0300 +Subject: [PATCH] arc add OWRTDTB section + +This change allows OpenWRT to patch resulting kernel binary with +external .dtb. + +That allows us to re-use exactky the same vmlinux on different boards +given its ARC core configurations match (at least cache line sizes etc). + +""patch-dtb" searches for ASCII "OWRTDTB:" strign and copies external +.dtb right after it, keeping the string in place. + +Signed-off-by: Eugeniy Paltsev +Signed-off-by: Alexey Brodkin +Signed-off-by: Evgeniy Didin +--- + arch/arc/kernel/head.S | 10 ++++++++++ + arch/arc/kernel/setup.c | 4 +++- + arch/arc/kernel/vmlinux.lds.S | 13 +++++++++++++ + 3 files changed, 26 insertions(+), 1 deletion(-) + +--- a/arch/arc/kernel/head.S ++++ b/arch/arc/kernel/head.S +@@ -88,6 +88,16 @@ + DSP_EARLY_INIT + .endm + ++ ; Here "patch-dtb" will embed external .dtb ++ ; Note "patch-dtb" searches for ASCII "OWRTDTB:" string ++ ; and pastes .dtb right after it, hense the string precedes ++ ; __image_dtb symbol. ++ .section .owrt, "aw",@progbits ++ .ascii "OWRTDTB:" ++ENTRY(__image_dtb) ++ .fill 0x4000 ++END(__image_dtb) ++ + .section .init.text, "ax",@progbits + + ;---------------------------------------------------------------- +--- a/arch/arc/kernel/setup.c ++++ b/arch/arc/kernel/setup.c +@@ -450,6 +450,8 @@ static inline bool uboot_arg_invalid(uns + /* We always pass 0 as magic from U-boot */ + #define UBOOT_MAGIC_VALUE 0 + ++extern struct boot_param_header __image_dtb; ++ + void __init handle_uboot_args(void) + { + bool use_embedded_dtb = true; +@@ -488,7 +490,7 @@ void __init handle_uboot_args(void) + ignore_uboot_args: + + if (use_embedded_dtb) { +- machine_desc = setup_machine_fdt(__dtb_start); ++ machine_desc = setup_machine_fdt(&__image_dtb); + if (!machine_desc) + panic("Embedded DT invalid\n"); + } +--- a/arch/arc/kernel/vmlinux.lds.S ++++ b/arch/arc/kernel/vmlinux.lds.S +@@ -27,6 +27,19 @@ SECTIONS + + . = CONFIG_LINUX_LINK_BASE; + ++ /* ++ * In OpenWRT we want to patch built binary embedding .dtb of choice. ++ * This is implemented with "patch-dtb" utility which searches for ++ * "OWRTDTB:" string in first 16k of image and if it is found ++ * copies .dtb right after mentioned string. ++ * ++ * Note: "OWRTDTB:" won't be overwritten with .dtb, .dtb will follow it. ++ */ ++ .owrt : { ++ *(.owrt) ++ . = ALIGN(PAGE_SIZE); ++ } ++ + _int_vec_base_lds = .; + .vector : { + *(.vector) diff --git a/target/linux/generic/pending-6.12/333-arc-enable-unaligned-access-in-kernel-mode.patch b/target/linux/generic/pending-6.12/333-arc-enable-unaligned-access-in-kernel-mode.patch new file mode 100644 index 00000000000..2c9808de2a3 --- /dev/null +++ b/target/linux/generic/pending-6.12/333-arc-enable-unaligned-access-in-kernel-mode.patch @@ -0,0 +1,24 @@ +From: Alexey Brodkin +Subject: arc: enable unaligned access in kernel mode + +This enables misaligned access handling even in kernel mode. +Some wireless drivers (ath9k-htc and mt7601u) use misaligned accesses +here and there and to cope with that without fixing stuff in the drivers +we're just gracefully handling it on ARC. + +Signed-off-by: Alexey Brodkin +--- + arch/arc/kernel/unaligned.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arc/kernel/unaligned.c ++++ b/arch/arc/kernel/unaligned.c +@@ -203,7 +203,7 @@ int misaligned_fixup(unsigned long addre + char buf[TASK_COMM_LEN]; + + /* handle user mode only and only if enabled by sysadmin */ +- if (!user_mode(regs) || !unaligned_enabled) ++ if (!unaligned_enabled) + return 1; + + if (no_unaligned_warning) { diff --git a/target/linux/generic/pending-6.12/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch b/target/linux/generic/pending-6.12/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch new file mode 100644 index 00000000000..c812a08b91d --- /dev/null +++ b/target/linux/generic/pending-6.12/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch @@ -0,0 +1,25 @@ +From 66770a004afe10df11d3902e16eaa0c2c39436bb Mon Sep 17 00:00:00 2001 +From: Pawel Dembicki +Date: Fri, 24 May 2019 17:56:19 +0200 +Subject: [PATCH] powerpc: Enable kernel XZ compression option on PPC_85xx + +Enable kernel XZ compression option on PPC_85xx. Tested with +simpleImage on TP-Link TL-WDR4900 (Freescale P1014 processor). + +Suggested-by: Christian Lamparter +Signed-off-by: Pawel Dembicki +--- + arch/powerpc/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/powerpc/Kconfig ++++ b/arch/powerpc/Kconfig +@@ -254,7 +254,7 @@ config PPC + select HAVE_KERNEL_GZIP + select HAVE_KERNEL_LZMA if DEFAULT_UIMAGE + select HAVE_KERNEL_LZO if DEFAULT_UIMAGE +- select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x ++ select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x || PPC_85xx + select HAVE_KPROBES + select HAVE_KPROBES_ON_FTRACE + select HAVE_KRETPROBES diff --git a/target/linux/generic/pending-6.12/350-mips-kernel-fix-detect_memory_region-function.patch b/target/linux/generic/pending-6.12/350-mips-kernel-fix-detect_memory_region-function.patch new file mode 100644 index 00000000000..a99eed4716a --- /dev/null +++ b/target/linux/generic/pending-6.12/350-mips-kernel-fix-detect_memory_region-function.patch @@ -0,0 +1,74 @@ +From: Shiji Yang +Date: Wed, 13 Mar 2024 20:28:37 +0800 +Subject: [PATCH] mips: kernel: fix detect_memory_region() function + +1. Do not use memcmp() on unallocated memory, as the new introduced + fortify dynamic object size check[1] will report unexpected result. +2. Use a fixed pattern instead of a random function pointer as the + magic value. +3. Flip magic value and double check it. +4. Enable this feature only for 32-bit CPUs. Currently, only ath79 and + ralink CPUs are using it. + +[1] 439a1bcac648 ("fortify: Use __builtin_dynamic_object_size() when available") +Signed-off-by: Shiji Yang +--- + arch/mips/include/asm/bootinfo.h | 2 ++ + arch/mips/kernel/setup.c | 17 ++++++++++++----- + 2 files changed, 14 insertions(+), 5 deletions(-) + +--- a/arch/mips/include/asm/bootinfo.h ++++ b/arch/mips/include/asm/bootinfo.h +@@ -93,7 +93,9 @@ const char *get_system_type(void); + + extern unsigned long mips_machtype; + ++#ifndef CONFIG_64BIT + extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max); ++#endif + + extern void prom_init(void); + extern void prom_free_prom_memory(void); +--- a/arch/mips/kernel/setup.c ++++ b/arch/mips/kernel/setup.c +@@ -86,21 +86,27 @@ static struct resource bss_resource = { + unsigned long __kaslr_offset __ro_after_init; + EXPORT_SYMBOL(__kaslr_offset); + +-static void *detect_magic __initdata = detect_memory_region; +- + #ifdef CONFIG_MIPS_AUTO_PFN_OFFSET + unsigned long ARCH_PFN_OFFSET; + EXPORT_SYMBOL(ARCH_PFN_OFFSET); + #endif + ++#ifndef CONFIG_64BIT ++static u32 detect_magic __initdata; ++#define MIPS_MEM_TEST_PATTERN 0xaa5555aa ++ + void __init detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max) + { +- void *dm = &detect_magic; ++ void *dm = (void *)KSEG1ADDR(&detect_magic); + phys_addr_t size; + + for (size = sz_min; size < sz_max; size <<= 1) { +- if (!memcmp(dm, dm + size, sizeof(detect_magic))) +- break; ++ __raw_writel(MIPS_MEM_TEST_PATTERN, dm); ++ if (__raw_readl(dm) == __raw_readl(dm + size)) { ++ __raw_writel(~MIPS_MEM_TEST_PATTERN, dm); ++ if (__raw_readl(dm) == __raw_readl(dm + size)) ++ break; ++ } + } + + pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: %lluMB)\n", +@@ -111,6 +117,7 @@ void __init detect_memory_region(phys_ad + + memblock_add(start, size); + } ++#endif /* CONFIG_64BIT */ + + /* + * Manage initrd diff --git a/target/linux/generic/pending-6.12/360-Revert-MIPS-mm-kmalloc-tlb_vpn-array-to-avoid-stack-.patch b/target/linux/generic/pending-6.12/360-Revert-MIPS-mm-kmalloc-tlb_vpn-array-to-avoid-stack-.patch new file mode 100644 index 00000000000..522fccd8e25 --- /dev/null +++ b/target/linux/generic/pending-6.12/360-Revert-MIPS-mm-kmalloc-tlb_vpn-array-to-avoid-stack-.patch @@ -0,0 +1,60 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Mon, 15 Dec 2025 01:45:07 +0100 +Subject: Revert "MIPS: mm: kmalloc tlb_vpn array to avoid stack overflow" + +This reverts commit 63a93d1cd6077d79735f804f5a4957bfb240280c. +--- + arch/mips/mm/tlb-r4k.c | 18 ++---------------- + 1 file changed, 2 insertions(+), 16 deletions(-) + +--- a/arch/mips/mm/tlb-r4k.c ++++ b/arch/mips/mm/tlb-r4k.c +@@ -12,7 +12,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -523,26 +522,17 @@ static int r4k_vpn_cmp(const void *a, co + * Initialise all TLB entries with unique values that do not clash with + * what we have been handed over and what we'll be using ourselves. + */ +-static void __ref r4k_tlb_uniquify(void) ++static void r4k_tlb_uniquify(void) + { ++ unsigned long tlb_vpns[1 << MIPS_CONF1_TLBS_SIZE]; + int tlbsize = current_cpu_data.tlbsize; +- bool use_slab = slab_is_available(); + int start = num_wired_entries(); +- phys_addr_t tlb_vpn_size; +- unsigned long *tlb_vpns; + unsigned long vpn_mask; + int cnt, ent, idx, i; + + vpn_mask = GENMASK(cpu_vmbits - 1, 13); + vpn_mask |= IS_ENABLED(CONFIG_64BIT) ? 3ULL << 62 : 1 << 31; + +- tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); +- tlb_vpns = (use_slab ? +- kmalloc(tlb_vpn_size, GFP_KERNEL) : +- memblock_alloc_raw(tlb_vpn_size, sizeof(*tlb_vpns))); +- if (WARN_ON(!tlb_vpns)) +- return; /* Pray local_flush_tlb_all() is good enough. */ +- + htw_stop(); + + for (i = start, cnt = 0; i < tlbsize; i++, cnt++) { +@@ -595,10 +585,6 @@ static void __ref r4k_tlb_uniquify(void) + tlbw_use_hazard(); + htw_start(); + flush_micro_tlb(); +- if (use_slab) +- kfree(tlb_vpns); +- else +- memblock_free(tlb_vpns, tlb_vpn_size); + } + + /* diff --git a/target/linux/generic/pending-6.12/361-Revert-MIPS-mm-Prevent-a-TLB-shutdown-on-initial-uni.patch b/target/linux/generic/pending-6.12/361-Revert-MIPS-mm-Prevent-a-TLB-shutdown-on-initial-uni.patch new file mode 100644 index 00000000000..826ed8efb2a --- /dev/null +++ b/target/linux/generic/pending-6.12/361-Revert-MIPS-mm-Prevent-a-TLB-shutdown-on-initial-uni.patch @@ -0,0 +1,146 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Mon, 15 Dec 2025 01:45:20 +0100 +Subject: Revert "MIPS: mm: Prevent a TLB shutdown on initial uniquification" + +This reverts commit 135178e90aa43ad949534e1d6e376c4034942caa. +--- + arch/mips/mm/tlb-r4k.c | 100 +++++++++++++++-------------------------- + 1 file changed, 37 insertions(+), 63 deletions(-) + +--- a/arch/mips/mm/tlb-r4k.c ++++ b/arch/mips/mm/tlb-r4k.c +@@ -15,7 +15,6 @@ + #include + #include + #include +-#include + + #include + #include +@@ -509,79 +508,55 @@ static int __init set_ntlb(char *str) + + __setup("ntlb=", set_ntlb); + +- +-/* Comparison function for EntryHi VPN fields. */ +-static int r4k_vpn_cmp(const void *a, const void *b) +-{ +- long v = *(unsigned long *)a - *(unsigned long *)b; +- int s = sizeof(long) > sizeof(int) ? sizeof(long) * 8 - 1: 0; +- return s ? (v != 0) | v >> s : v; +-} +- +-/* +- * Initialise all TLB entries with unique values that do not clash with +- * what we have been handed over and what we'll be using ourselves. +- */ ++/* Initialise all TLB entries with unique values */ + static void r4k_tlb_uniquify(void) + { +- unsigned long tlb_vpns[1 << MIPS_CONF1_TLBS_SIZE]; +- int tlbsize = current_cpu_data.tlbsize; +- int start = num_wired_entries(); +- unsigned long vpn_mask; +- int cnt, ent, idx, i; +- +- vpn_mask = GENMASK(cpu_vmbits - 1, 13); +- vpn_mask |= IS_ENABLED(CONFIG_64BIT) ? 3ULL << 62 : 1 << 31; ++ int entry = num_wired_entries(); + + htw_stop(); ++ write_c0_entrylo0(0); ++ write_c0_entrylo1(0); + +- for (i = start, cnt = 0; i < tlbsize; i++, cnt++) { +- unsigned long vpn; ++ while (entry < current_cpu_data.tlbsize) { ++ unsigned long asid_mask = cpu_asid_mask(¤t_cpu_data); ++ unsigned long asid = 0; ++ int idx; + +- write_c0_index(i); +- mtc0_tlbr_hazard(); +- tlb_read(); +- tlb_read_hazard(); +- vpn = read_c0_entryhi(); +- vpn &= vpn_mask & PAGE_MASK; +- tlb_vpns[cnt] = vpn; ++ /* Skip wired MMID to make ginvt_mmid work */ ++ if (cpu_has_mmid) ++ asid = MMID_KERNEL_WIRED + 1; + +- /* Prevent any large pages from overlapping regular ones. */ +- write_c0_pagemask(read_c0_pagemask() & PM_DEFAULT_MASK); ++ /* Check for match before using UNIQUE_ENTRYHI */ ++ do { ++ if (cpu_has_mmid) { ++ write_c0_memorymapid(asid); ++ write_c0_entryhi(UNIQUE_ENTRYHI(entry)); ++ } else { ++ write_c0_entryhi(UNIQUE_ENTRYHI(entry) | asid); ++ } ++ mtc0_tlbw_hazard(); ++ tlb_probe(); ++ tlb_probe_hazard(); ++ idx = read_c0_index(); ++ /* No match or match is on current entry */ ++ if (idx < 0 || idx == entry) ++ break; ++ /* ++ * If we hit a match, we need to try again with ++ * a different ASID. ++ */ ++ asid++; ++ } while (asid < asid_mask); ++ ++ if (idx >= 0 && idx != entry) ++ panic("Unable to uniquify TLB entry %d", idx); ++ ++ write_c0_index(entry); + mtc0_tlbw_hazard(); + tlb_write_indexed(); +- tlbw_use_hazard(); ++ entry++; + } + +- sort(tlb_vpns, cnt, sizeof(tlb_vpns[0]), r4k_vpn_cmp, NULL); +- +- write_c0_pagemask(PM_DEFAULT_MASK); +- write_c0_entrylo0(0); +- write_c0_entrylo1(0); +- +- idx = 0; +- ent = tlbsize; +- for (i = start; i < tlbsize; i++) +- while (1) { +- unsigned long entryhi, vpn; +- +- entryhi = UNIQUE_ENTRYHI(ent); +- vpn = entryhi & vpn_mask & PAGE_MASK; +- +- if (idx >= cnt || vpn < tlb_vpns[idx]) { +- write_c0_entryhi(entryhi); +- write_c0_index(i); +- mtc0_tlbw_hazard(); +- tlb_write_indexed(); +- ent++; +- break; +- } else if (vpn == tlb_vpns[idx]) { +- ent++; +- } else { +- idx++; +- } +- } +- + tlbw_use_hazard(); + htw_start(); + flush_micro_tlb(); +@@ -627,7 +602,6 @@ static void r4k_tlb_configure(void) + + /* From this point on the ARC firmware is dead. */ + r4k_tlb_uniquify(); +- local_flush_tlb_all(); + + /* Did I tell you that ARC SUCKS? */ + } diff --git a/target/linux/generic/pending-6.12/400-mtd-mtdsplit-support.patch b/target/linux/generic/pending-6.12/400-mtd-mtdsplit-support.patch new file mode 100644 index 00000000000..2a3a285a821 --- /dev/null +++ b/target/linux/generic/pending-6.12/400-mtd-mtdsplit-support.patch @@ -0,0 +1,328 @@ +From 39717277d5c87bdb183cf2f258957b44ba99b4df Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 11:47:35 +0200 +Subject: [PATCH] mtd: mtdsplit support + +--- + drivers/mtd/Kconfig | 19 ++++ + drivers/mtd/Makefile | 2 + + drivers/mtd/mtdpart.c | 169 ++++++++++++++++++++++++++++----- + include/linux/mtd/mtd.h | 25 +++++ + include/linux/mtd/partitions.h | 7 ++ + 5 files changed, 197 insertions(+), 25 deletions(-) + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -12,6 +12,25 @@ menuconfig MTD + + if MTD + ++menu "OpenWrt specific MTD options" ++ ++config MTD_ROOTFS_ROOT_DEV ++ bool "Automatically set 'rootfs' partition to be root filesystem" ++ default y ++ ++config MTD_SPLIT_FIRMWARE ++ bool "Automatically split firmware partition for kernel+rootfs" ++ default y ++ ++config MTD_SPLIT_FIRMWARE_NAME ++ string "Firmware partition name" ++ depends on MTD_SPLIT_FIRMWARE ++ default "firmware" ++ ++source "drivers/mtd/mtdsplit/Kconfig" ++ ++endmenu ++ + config MTD_TESTS + tristate "MTD tests support (DANGEROUS)" + depends on m +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconc + + obj-y += parsers/ + ++obj-$(CONFIG_MTD_SPLIT) += mtdsplit/ ++ + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o + obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -15,11 +15,13 @@ + #include + #include + #include ++#include + #include + #include + #include + + #include "mtdcore.h" ++#include "mtdsplit/mtdsplit.h" + + /* + * MTD methods which simply translate the effective address and pass through +@@ -242,6 +244,147 @@ static int mtd_add_partition_attrs(struc + return ret; + } + ++static DEFINE_SPINLOCK(part_parser_lock); ++static LIST_HEAD(part_parsers); ++ ++static struct mtd_part_parser *mtd_part_parser_get(const char *name) ++{ ++ struct mtd_part_parser *p, *ret = NULL; ++ ++ spin_lock(&part_parser_lock); ++ ++ list_for_each_entry(p, &part_parsers, list) ++ if (!strcmp(p->name, name) && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++static inline void mtd_part_parser_put(const struct mtd_part_parser *p) ++{ ++ module_put(p->owner); ++} ++ ++static struct mtd_part_parser * ++get_partition_parser_by_type(enum mtd_parser_type type, ++ struct mtd_part_parser *start) ++{ ++ struct mtd_part_parser *p, *ret = NULL; ++ ++ spin_lock(&part_parser_lock); ++ ++ p = list_prepare_entry(start, &part_parsers, list); ++ if (start) ++ mtd_part_parser_put(start); ++ ++ list_for_each_entry_continue(p, &part_parsers, list) { ++ if (p->type == type && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ } ++ ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++static int parse_mtd_partitions_by_type(struct mtd_info *master, ++ enum mtd_parser_type type, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct mtd_part_parser *prev = NULL; ++ int ret = 0; ++ ++ while (1) { ++ struct mtd_part_parser *parser; ++ ++ parser = get_partition_parser_by_type(type, prev); ++ if (!parser) ++ break; ++ ++ ret = (*parser->parse_fn)(master, pparts, data); ++ ++ if (ret > 0) { ++ mtd_part_parser_put(parser); ++ printk(KERN_NOTICE ++ "%d %s partitions found on MTD device %s\n", ++ ret, parser->name, master->name); ++ break; ++ } ++ ++ prev = parser; ++ } ++ ++ return ret; ++} ++ ++static int ++run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type) ++{ ++ struct mtd_partition *parts; ++ int nr_parts; ++ int i; ++ ++ nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts, ++ NULL); ++ if (nr_parts <= 0) ++ return nr_parts; ++ ++ if (WARN_ON(!parts)) ++ return 0; ++ ++ for (i = 0; i < nr_parts; i++) { ++ /* adjust partition offsets */ ++ parts[i].offset += child->part.offset; ++ ++ mtd_add_partition(child->parent, ++ parts[i].name, ++ parts[i].offset, ++ parts[i].size); ++ } ++ ++ kfree(parts); ++ ++ return nr_parts; ++} ++ ++#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME ++#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME ++#else ++#define SPLIT_FIRMWARE_NAME "unused" ++#endif ++ ++static void split_firmware(struct mtd_info *master, struct mtd_info *part) ++{ ++ run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE); ++} ++ ++static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part) ++{ ++ static int rootfs_found = 0; ++ ++ if (rootfs_found) ++ return; ++ ++ if (of_property_present(mtd_get_of_node(part), "linux,rootfs") || ++ !strcmp(part->name, "rootfs")) { ++ run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS); ++ ++ rootfs_found = 1; ++ } ++ ++ if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) && ++ !strcmp(part->name, SPLIT_FIRMWARE_NAME) && ++ !of_property_present(mtd_get_of_node(part), "compatible")) ++ split_firmware(master, part); ++} ++ + int mtd_add_partition(struct mtd_info *parent, const char *name, + long long offset, long long length) + { +@@ -280,6 +423,7 @@ int mtd_add_partition(struct mtd_info *p + if (ret) + goto err_remove_part; + ++ mtd_partition_split(parent, child); + mtd_add_partition_attrs(child); + + return 0; +@@ -423,6 +567,7 @@ int add_mtd_partitions(struct mtd_info * + goto err_del_partitions; + } + ++ mtd_partition_split(master, child); + mtd_add_partition_attrs(child); + + /* Look for subpartitions (skip if no maching parser found) */ +@@ -446,31 +591,6 @@ err_del_partitions: + return ret; + } + +-static DEFINE_SPINLOCK(part_parser_lock); +-static LIST_HEAD(part_parsers); +- +-static struct mtd_part_parser *mtd_part_parser_get(const char *name) +-{ +- struct mtd_part_parser *p, *ret = NULL; +- +- spin_lock(&part_parser_lock); +- +- list_for_each_entry(p, &part_parsers, list) +- if (!strcmp(p->name, name) && try_module_get(p->owner)) { +- ret = p; +- break; +- } +- +- spin_unlock(&part_parser_lock); +- +- return ret; +-} +- +-static inline void mtd_part_parser_put(const struct mtd_part_parser *p) +-{ +- module_put(p->owner); +-} +- + /* + * Many partition parsers just expected the core to kfree() all their data in + * one chunk. Do that by default. +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -615,6 +615,24 @@ static inline void mtd_align_erase_req(s + req->len += mtd->erasesize - mod; + } + ++static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd) ++{ ++ if (mtd_mod_by_eb(sz, mtd) == 0) ++ return sz; ++ ++ /* Round up to next erase block */ ++ return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize; ++} ++ ++static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd) ++{ ++ if (mtd_mod_by_eb(sz, mtd) == 0) ++ return sz; ++ ++ /* Round down to the start of the current erase block */ ++ return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize; ++} ++ + static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd) + { + if (mtd->writesize_shift) +@@ -688,6 +706,13 @@ extern struct mtd_info *of_get_mtd_devic + extern struct mtd_info *get_mtd_device_nm(const char *name); + extern void put_mtd_device(struct mtd_info *mtd); + ++static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd) ++{ ++ if (!mtd_is_partition(mtd)) ++ return 0; ++ ++ return mtd->part.offset; ++} + + struct mtd_notifier { + void (*add)(struct mtd_info *mtd); +--- a/include/linux/mtd/partitions.h ++++ b/include/linux/mtd/partitions.h +@@ -75,6 +75,12 @@ struct mtd_part_parser_data { + * Functions dealing with the various ways of partitioning the space + */ + ++enum mtd_parser_type { ++ MTD_PARSER_TYPE_DEVICE = 0, ++ MTD_PARSER_TYPE_ROOTFS, ++ MTD_PARSER_TYPE_FIRMWARE, ++}; ++ + struct mtd_part_parser { + struct list_head list; + struct module *owner; +@@ -83,6 +89,7 @@ struct mtd_part_parser { + int (*parse_fn)(struct mtd_info *, const struct mtd_partition **, + struct mtd_part_parser_data *); + void (*cleanup)(const struct mtd_partition *pparts, int nr_parts); ++ enum mtd_parser_type type; + }; + + /* Container for passing around a set of parsed partitions */ diff --git a/target/linux/generic/pending-6.12/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch b/target/linux/generic/pending-6.12/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch new file mode 100644 index 00000000000..e8ad2043373 --- /dev/null +++ b/target/linux/generic/pending-6.12/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch @@ -0,0 +1,245 @@ +From acacdac272927ae1d96e0bca51eb82899671eaea Mon Sep 17 00:00:00 2001 +From: John Thomson +Date: Fri, 25 Dec 2020 18:50:08 +1000 +Subject: [PATCH] mtd: spi-nor: write support for minor aligned partitions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Do not prevent writing to mtd partitions where a partition boundary sits +on a minor erasesize boundary. +This addresses a FIXME that has been present since the start of the +linux git history: +/* Doesn't start on a boundary of major erase size */ +/* FIXME: Let it be writable if it is on a boundary of + * _minor_ erase size though */ + +Allow a uniform erase region spi-nor device to be configured +to use the non-uniform erase regions code path for an erase with: +CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y + +On supporting hardware (SECT_4K: majority of current SPI-NOR device) +provide the facility for an erase to use the least number +of SPI-NOR operations, as well as access to 4K erase without +requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + +Introduce erasesize_minor to the mtd struct, +the smallest erasesize supported by the device + +On existing devices, this is useful where write support is wanted +for data on a 4K partition, such as some u-boot-env partitions, +or RouterBoot soft_config, while still netting the performance +benefits of using 64K sectors + +Performance: +time mtd erase firmware +OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length + +Without this patch +MTD_SPI_NOR_USE_4K_SECTORS=y |n +real 2m 11.66s |0m 50.86s +user 0m 0.00s |0m 0.00s +sys 1m 56.20s |0m 50.80s + +With this patch +MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y +real 0m 51.68s |0m 50.85s |2m 12.89s +user 0m 0.00s |0m 0.00s |0m 0.01s +sys 0m 46.94s |0m 50.38s |2m 12.46s + +Signed-off-by: John Thomson +Signed-off-by: Thibaut VARÈNE + +--- + +checkpatch does not like the printk(KERN_WARNING +these should be changed separately beforehand? + +Changes v1 -> v2: +Added mtdcore sysfs for erasesize_minor +Removed finding minor erasesize for variable erase regions device, +as untested and no responses regarding it. +Moved IF_ENABLED for SPINOR variable erase to guard setting +erasesize_minor in spi-nor/core.c +Removed setting erasesize to minor where partition boundaries require +minor erase to be writable +Simplified minor boundary check by relying on minor being a factor of +major + +Changes RFC -> v1: +Fix uninitialized variable smatch warning +Reported-by: kernel test robot +Reported-by: Dan Carpenter +--- + drivers/mtd/mtdcore.c | 10 ++++++++++ + drivers/mtd/mtdpart.c | 35 +++++++++++++++++++++++++---------- + drivers/mtd/spi-nor/Kconfig | 10 ++++++++++ + drivers/mtd/spi-nor/core.c | 11 +++++++++-- + include/linux/mtd/mtd.h | 2 ++ + 5 files changed, 56 insertions(+), 12 deletions(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -199,6 +199,15 @@ static ssize_t mtd_erasesize_show(struct + } + MTD_DEVICE_ATTR_RO(erasesize); + ++static ssize_t mtd_erasesize_minor_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mtd_info *mtd = dev_get_drvdata(dev); ++ ++ return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize_minor); ++} ++MTD_DEVICE_ATTR_RO(erasesize_minor); ++ + static ssize_t mtd_writesize_show(struct device *dev, + struct device_attribute *attr, char *buf) + { +@@ -344,6 +353,7 @@ static struct attribute *mtd_attrs[] = { + &dev_attr_flags.attr, + &dev_attr_size.attr, + &dev_attr_erasesize.attr, ++ &dev_attr_erasesize_minor.attr, + &dev_attr_writesize.attr, + &dev_attr_subpagesize.attr, + &dev_attr_oobsize.attr, +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -47,6 +47,7 @@ static struct mtd_info *allocate_partiti + struct mtd_info *master = mtd_get_master(parent); + int wr_alignment = (parent->flags & MTD_NO_ERASE) ? + master->writesize : master->erasesize; ++ int wr_alignment_minor = 0; + u64 parent_size = mtd_is_partition(parent) ? + parent->part.size : parent->size; + struct mtd_info *child; +@@ -171,6 +172,7 @@ static struct mtd_info *allocate_partiti + } else { + /* Single erase size */ + child->erasesize = master->erasesize; ++ child->erasesize_minor = master->erasesize_minor; + } + + /* +@@ -178,26 +180,39 @@ static struct mtd_info *allocate_partiti + * exposes several regions with different erasesize. Adjust + * wr_alignment accordingly. + */ +- if (!(child->flags & MTD_NO_ERASE)) ++ if (!(child->flags & MTD_NO_ERASE)) { + wr_alignment = child->erasesize; ++ wr_alignment_minor = child->erasesize_minor; ++ } + + tmp = mtd_get_master_ofs(child, 0); + remainder = do_div(tmp, wr_alignment); + if ((child->flags & MTD_WRITEABLE) && remainder) { +- /* Doesn't start on a boundary of major erase size */ +- /* FIXME: Let it be writable if it is on a boundary of +- * _minor_ erase size though */ +- child->flags &= ~MTD_WRITEABLE; +- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", +- part->name); ++ if (wr_alignment_minor) { ++ /* rely on minor being a factor of major erasesize */ ++ tmp = remainder; ++ remainder = do_div(tmp, wr_alignment_minor); ++ } ++ if (remainder) { ++ child->flags &= ~MTD_WRITEABLE; ++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", ++ part->name); ++ } + } + + tmp = mtd_get_master_ofs(child, 0) + child->part.size; + remainder = do_div(tmp, wr_alignment); + if ((child->flags & MTD_WRITEABLE) && remainder) { +- child->flags &= ~MTD_WRITEABLE; +- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", +- part->name); ++ if (wr_alignment_minor) { ++ tmp = remainder; ++ remainder = do_div(tmp, wr_alignment_minor); ++ } ++ ++ if (remainder) { ++ child->flags &= ~MTD_WRITEABLE; ++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", ++ part->name); ++ } + } + + child->size = child->part.size; +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR + + if MTD_SPI_NOR + ++config MTD_SPI_NOR_USE_VARIABLE_ERASE ++ bool "Disable uniform_erase to allow use of all hardware supported erasesizes" ++ depends on !MTD_SPI_NOR_USE_4K_SECTORS ++ default n ++ help ++ Allow mixed use of all hardware supported erasesizes, ++ by forcing spi_nor to use the multiple eraseregions code path. ++ For example: A 68K erase will use one 64K erase, and one 4K erase ++ on supporting hardware. ++ + config MTD_SPI_NOR_USE_4K_SECTORS + bool "Use small 4096 B erase sectors" + default y +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -1158,6 +1158,8 @@ static u8 spi_nor_convert_3to4_erase(u8 + + static bool spi_nor_has_uniform_erase(const struct spi_nor *nor) + { ++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE)) ++ return false; + return !!nor->params->erase_map.uniform_region.erase_mask; + } + +@@ -2516,6 +2518,7 @@ static int spi_nor_select_erase(struct s + { + struct spi_nor_erase_map *map = &nor->params->erase_map; + const struct spi_nor_erase_type *erase = NULL; ++ const struct spi_nor_erase_type *erase_minor = NULL; + struct mtd_info *mtd = &nor->mtd; + int i; + +@@ -2542,8 +2545,9 @@ static int spi_nor_select_erase(struct s + */ + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) { + if (map->erase_type[i].size) { +- erase = &map->erase_type[i]; +- break; ++ if (!erase) ++ erase = &map->erase_type[i]; ++ erase_minor = &map->erase_type[i]; + } + } + +@@ -2551,6 +2555,9 @@ static int spi_nor_select_erase(struct s + return -EINVAL; + + mtd->erasesize = erase->size; ++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) && ++ erase_minor && erase_minor->size < erase->size) ++ mtd->erasesize_minor = erase_minor->size; + return 0; + } + +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -245,6 +245,8 @@ struct mtd_info { + * information below if they desire + */ + uint32_t erasesize; ++ /* "Minor" (smallest) erase size supported by the whole device */ ++ uint32_t erasesize_minor; + /* Minimal writable flash unit size. In case of NOR flash it is 1 (even + * though individual bits can be cleared), in case of NAND flash it is + * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR diff --git a/target/linux/generic/pending-6.12/417-mtd-spi-nand-macronix-disable-continuous-read-for-MX.patch b/target/linux/generic/pending-6.12/417-mtd-spi-nand-macronix-disable-continuous-read-for-MX.patch new file mode 100644 index 00000000000..65531e099d2 --- /dev/null +++ b/target/linux/generic/pending-6.12/417-mtd-spi-nand-macronix-disable-continuous-read-for-MX.patch @@ -0,0 +1,39 @@ +From 435afd182e8f5997033da1d69e56094ecb112cad Mon Sep 17 00:00:00 2001 +From: Chukun Pan +Date: Thu, 10 Jun 2025 23:10:16 +0800 +Subject: [PATCH] mtd: spi-nand: macronix: disable continuous read for + MX35LFxGE4AD + +In on-die ECC mode, enabling continuous read will cause ubi_io_read error. +[ 7.852000] ubi0 warning: ubi_io_read: error -5 while reading ... + +So disable it first. Tested on GL.iNet GL-MT3000 with MX35LF2GE4AD flash. + +Fixes: 11813857864f ("mtd: spi-nand: macronix: Continuous read support") +Signed-off-by: Chukun Pan +--- + drivers/mtd/nand/spi/macronix.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/mtd/nand/spi/macronix.c ++++ b/drivers/mtd/nand/spi/macronix.c +@@ -167,8 +167,7 @@ static const struct spinand_info macroni + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, +- macronix_ecc_get_status), +- SPINAND_CONT_READ(macronix_set_cont_read)), ++ macronix_ecc_get_status)), + SPINAND_INFO("MX35LF4GE4AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37, 0x03), + NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1), +@@ -178,8 +177,7 @@ static const struct spinand_info macroni + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, +- macronix_ecc_get_status), +- SPINAND_CONT_READ(macronix_set_cont_read)), ++ macronix_ecc_get_status)), + SPINAND_INFO("MX35LF1G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14, 0x03), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), diff --git a/target/linux/generic/pending-6.12/420-mtd-redboot_space.patch b/target/linux/generic/pending-6.12/420-mtd-redboot_space.patch new file mode 100644 index 00000000000..5518ea71dd5 --- /dev/null +++ b/target/linux/generic/pending-6.12/420-mtd-redboot_space.patch @@ -0,0 +1,41 @@ +From: Felix Fietkau +Subject: add patch for including unpartitioned space in the rootfs partition for redboot devices (if applicable) + +[john@phrozen.org: used by ixp and others] + +lede-commit: 394918851f84e4d00fa16eb900e7700e95091f00 +Signed-off-by: Felix Fietkau +--- + drivers/mtd/redboot.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +--- a/drivers/mtd/parsers/redboot.c ++++ b/drivers/mtd/parsers/redboot.c +@@ -278,14 +278,21 @@ nogood: + #endif + names += strlen(names) + 1; + +-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { +- i++; +- parts[i].offset = parts[i - 1].size + parts[i - 1].offset; +- parts[i].size = fl->next->img->flash_base - parts[i].offset; +- parts[i].name = nullname; +- } ++ if (!strcmp(parts[i].name, "rootfs")) { ++ parts[i].size = fl->next->img->flash_base; ++ parts[i].size &= ~(master->erasesize - 1); ++ parts[i].size -= parts[i].offset; ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ nrparts--; ++ } else { ++ i++; ++ parts[i].offset = parts[i-1].size + parts[i-1].offset; ++ parts[i].size = fl->next->img->flash_base - parts[i].offset; ++ parts[i].name = nullname; + #endif ++ } ++ } + tmp_fl = fl; + fl = fl->next; + kfree(tmp_fl); diff --git a/target/linux/generic/pending-6.12/430-mtd-add-myloader-partition-parser.patch b/target/linux/generic/pending-6.12/430-mtd-add-myloader-partition-parser.patch new file mode 100644 index 00000000000..8ce112357a2 --- /dev/null +++ b/target/linux/generic/pending-6.12/430-mtd-add-myloader-partition-parser.patch @@ -0,0 +1,229 @@ +From: Florian Fainelli +Subject: Add myloader partition table parser + +[john@phozen.org: shoud be upstreamable] + +lede-commit: d8bf22859b51faa09d22c056fe221a45d2f7a3b8 +Signed-off-by: Florian Fainelli +[adjust for kernel 5.4, add myloader.c to patch] +Signed-off-by: Adrian Schmutzler + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -62,6 +62,22 @@ config MTD_CMDLINE_PARTS + + If unsure, say 'N'. + ++config MTD_MYLOADER_PARTS ++ tristate "MyLoader partition parsing" ++ depends on ADM5120 || ATH79 ++ help ++ MyLoader is a bootloader which allows the user to define partitions ++ in flash devices, by putting a table in the second erase block ++ on the device, similar to a partition table. This table gives the ++ offsets and lengths of the user defined partitions. ++ ++ If you need code which can detect and parse these tables, and ++ register MTD 'partitions' corresponding to each image detected, ++ enable this option. ++ ++ You will still need the parsing functions to be called by the driver ++ for your particular device. It won't happen automatically. ++ + config MTD_OF_PARTS + tristate "OpenFirmware (device tree) partitioning parser" + default y +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -3,6 +3,7 @@ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm4 + obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o + obj-$(CONFIG_MTD_BRCM_U_BOOT) += brcm_u-boot.o + obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o ++obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o + obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o + ofpart-y += ofpart_core.o + ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o +--- /dev/null ++++ b/drivers/mtd/parsers/myloader.c +@@ -0,0 +1,181 @@ ++/* ++ * Parse MyLoader-style flash partition tables and produce a Linux partition ++ * array to match. ++ * ++ * Copyright (C) 2007-2009 Gabor Juhos ++ * ++ * This file was based on drivers/mtd/redboot.c ++ * Author: Red Hat, Inc. - David Woodhouse ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BLOCK_LEN_MIN 0x10000 ++#define PART_NAME_LEN 32 ++ ++struct part_data { ++ struct mylo_partition_table tab; ++ char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN]; ++}; ++ ++static int myloader_parse_partitions(struct mtd_info *master, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct part_data *buf; ++ struct mylo_partition_table *tab; ++ struct mylo_partition *part; ++ struct mtd_partition *mtd_parts; ++ struct mtd_partition *mtd_part; ++ int num_parts; ++ int ret, i; ++ size_t retlen; ++ char *names; ++ unsigned long offset; ++ unsigned long blocklen; ++ ++ buf = vmalloc(sizeof(*buf)); ++ if (!buf) { ++ return -ENOMEM; ++ goto out; ++ } ++ tab = &buf->tab; ++ ++ blocklen = master->erasesize; ++ if (blocklen < BLOCK_LEN_MIN) ++ blocklen = BLOCK_LEN_MIN; ++ ++ offset = blocklen; ++ ++ /* Find the partition table */ ++ for (i = 0; i < 4; i++, offset += blocklen) { ++ printk(KERN_DEBUG "%s: searching for MyLoader partition table" ++ " at offset 0x%lx\n", master->name, offset); ++ ++ ret = mtd_read(master, offset, sizeof(*buf), &retlen, ++ (void *)buf); ++ if (ret) ++ goto out_free_buf; ++ ++ if (retlen != sizeof(*buf)) { ++ ret = -EIO; ++ goto out_free_buf; ++ } ++ ++ /* Check for Partition Table magic number */ ++ if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS)) ++ break; ++ ++ } ++ ++ if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) { ++ printk(KERN_DEBUG "%s: no MyLoader partition table found\n", ++ master->name); ++ ret = 0; ++ goto out_free_buf; ++ } ++ ++ /* The MyLoader and the Partition Table is always present */ ++ num_parts = 2; ++ ++ /* Detect number of used partitions */ ++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { ++ part = &tab->partitions[i]; ++ ++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) ++ continue; ++ ++ num_parts++; ++ } ++ ++ mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) + ++ num_parts * PART_NAME_LEN), GFP_KERNEL); ++ ++ if (!mtd_parts) { ++ ret = -ENOMEM; ++ goto out_free_buf; ++ } ++ ++ mtd_part = mtd_parts; ++ names = (char *)&mtd_parts[num_parts]; ++ ++ strncpy(names, "myloader", PART_NAME_LEN); ++ mtd_part->name = names; ++ mtd_part->offset = 0; ++ mtd_part->size = offset; ++ mtd_part->mask_flags = MTD_WRITEABLE; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ ++ strncpy(names, "partition_table", PART_NAME_LEN); ++ mtd_part->name = names; ++ mtd_part->offset = offset; ++ mtd_part->size = blocklen; ++ mtd_part->mask_flags = MTD_WRITEABLE; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ ++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { ++ part = &tab->partitions[i]; ++ ++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) ++ continue; ++ ++ if ((buf->names[i][0]) && (buf->names[i][0] != '\xff')) ++ strncpy(names, buf->names[i], PART_NAME_LEN); ++ else ++ snprintf(names, PART_NAME_LEN, "partition%d", i); ++ ++ mtd_part->offset = le32_to_cpu(part->addr); ++ mtd_part->size = le32_to_cpu(part->size); ++ mtd_part->name = names; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ } ++ ++ *pparts = mtd_parts; ++ ret = num_parts; ++ ++ out_free_buf: ++ vfree(buf); ++ out: ++ return ret; ++} ++ ++static struct mtd_part_parser myloader_mtd_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = myloader_parse_partitions, ++ .name = "MyLoader", ++}; ++ ++static int __init myloader_mtd_parser_init(void) ++{ ++ register_mtd_parser(&myloader_mtd_parser); ++ ++ return 0; ++} ++ ++static void __exit myloader_mtd_parser_exit(void) ++{ ++ deregister_mtd_parser(&myloader_mtd_parser); ++} ++ ++module_init(myloader_mtd_parser_init); ++module_exit(myloader_mtd_parser_exit); ++ ++MODULE_AUTHOR("Gabor Juhos "); ++MODULE_DESCRIPTION("Parsing code for MyLoader partition tables"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/generic/pending-6.12/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch b/target/linux/generic/pending-6.12/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch new file mode 100644 index 00000000000..bcea45d009b --- /dev/null +++ b/target/linux/generic/pending-6.12/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch @@ -0,0 +1,68 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Subject: [PATCH] mtd: bcm47xxpart: check for bad blocks when calculating offsets + +Signed-off-by: Rafał Miłecki +--- + +--- a/drivers/mtd/parsers/parser_trx.c ++++ b/drivers/mtd/parsers/parser_trx.c +@@ -25,6 +25,33 @@ struct trx_header { + uint32_t offset[3]; + } __packed; + ++/* ++ * Calculate real end offset (address) for a given amount of data. It checks ++ * all blocks skipping bad ones. ++ */ ++static size_t parser_trx_real_offset(struct mtd_info *mtd, size_t bytes) ++{ ++ size_t real_offset = 0; ++ ++ if (mtd_block_isbad(mtd, real_offset)) ++ pr_warn("Base offset shouldn't be at bad block"); ++ ++ while (bytes >= mtd->erasesize) { ++ bytes -= mtd->erasesize; ++ real_offset += mtd->erasesize; ++ while (mtd_block_isbad(mtd, real_offset)) { ++ real_offset += mtd->erasesize; ++ ++ if (real_offset >= mtd->size) ++ return real_offset - mtd->erasesize; ++ } ++ } ++ ++ real_offset += bytes; ++ ++ return real_offset; ++} ++ + static const char *parser_trx_data_part_name(struct mtd_info *master, + size_t offset) + { +@@ -86,21 +113,21 @@ static int parser_trx_parse(struct mtd_i + if (trx.offset[2]) { + part = &parts[curr_part++]; + part->name = "loader"; +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); + i++; + } + + if (trx.offset[i]) { + part = &parts[curr_part++]; + part->name = "linux"; +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); + i++; + } + + if (trx.offset[i]) { + part = &parts[curr_part++]; +- part->name = parser_trx_data_part_name(mtd, trx.offset[i]); +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); ++ part->name = parser_trx_data_part_name(mtd, part->offset); + i++; + } + diff --git a/target/linux/generic/pending-6.12/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch b/target/linux/generic/pending-6.12/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch new file mode 100644 index 00000000000..852654d924a --- /dev/null +++ b/target/linux/generic/pending-6.12/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch @@ -0,0 +1,37 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Subject: mtd: bcm47xxpart: detect T_Meter partition + +It can be found on many Netgear devices. It consists of many 0x30 blocks +starting with 4D 54. + +Signed-off-by: Rafał Miłecki +--- + drivers/mtd/bcm47xxpart.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/mtd/parsers/bcm47xxpart.c ++++ b/drivers/mtd/parsers/bcm47xxpart.c +@@ -35,6 +35,7 @@ + #define NVRAM_HEADER 0x48534C46 /* FLSH */ + #define POT_MAGIC1 0x54544f50 /* POTT */ + #define POT_MAGIC2 0x504f /* OP */ ++#define T_METER_MAGIC 0x4D540000 /* MT */ + #define ML_MAGIC1 0x39685a42 + #define ML_MAGIC2 0x26594131 + #define TRX_MAGIC 0x30524448 +@@ -178,6 +179,15 @@ static int bcm47xxpart_parse(struct mtd_ + MTD_WRITEABLE); + continue; + } ++ ++ /* T_Meter */ ++ if ((le32_to_cpu(buf[0x000 / 4]) & 0xFFFF0000) == T_METER_MAGIC && ++ (le32_to_cpu(buf[0x030 / 4]) & 0xFFFF0000) == T_METER_MAGIC && ++ (le32_to_cpu(buf[0x060 / 4]) & 0xFFFF0000) == T_METER_MAGIC) { ++ bcm47xxpart_add_part(&parts[curr_part++], "T_Meter", offset, ++ MTD_WRITEABLE); ++ continue; ++ } + + /* TRX */ + if (buf[0x000 / 4] == TRX_MAGIC) { diff --git a/target/linux/generic/pending-6.12/435-mtd-add-routerbootpart-parser-config.patch b/target/linux/generic/pending-6.12/435-mtd-add-routerbootpart-parser-config.patch new file mode 100644 index 00000000000..d516e1dc78d --- /dev/null +++ b/target/linux/generic/pending-6.12/435-mtd-add-routerbootpart-parser-config.patch @@ -0,0 +1,38 @@ +From 4437e01fb6bca63fccdba5d6c44888b0935885c2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= +Date: Tue, 24 Mar 2020 11:45:07 +0100 +Subject: [PATCH] generic: routerboot partition build bits (5.4) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch adds routerbootpart kernel build bits + +Signed-off-by: Thibaut VARÈNE +--- + drivers/mtd/parsers/Kconfig | 9 +++++++++ + drivers/mtd/parsers/Makefile | 1 + + 2 files changed, 10 insertions(+) + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -231,3 +231,12 @@ config MTD_SERCOMM_PARTS + partition map. This partition table contains real partition + offsets, which may differ from device to device depending on the + number and location of bad blocks on NAND. ++ ++config MTD_ROUTERBOOT_PARTS ++ tristate "RouterBoot flash partition parser" ++ depends on MTD && OF ++ help ++ MikroTik RouterBoot is implemented as a multi segment system on the ++ flash, some of which are fixed and some of which are located at ++ variable offsets. This parser handles both cases via properly ++ formatted DTS. +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpa + obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o + obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o + obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o ++obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o diff --git a/target/linux/generic/pending-6.12/450-block-allow-setting-partition-of_node.patch b/target/linux/generic/pending-6.12/450-block-allow-setting-partition-of_node.patch new file mode 100644 index 00000000000..a3c8abc1d9d --- /dev/null +++ b/target/linux/generic/pending-6.12/450-block-allow-setting-partition-of_node.patch @@ -0,0 +1,123 @@ +From decc6959a423c8617e87244fd269129fc4e7d0e6 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Oct 2024 23:36:14 +0100 +Subject: [PATCH 1/8] block: allow setting partition of_node + +Allow partition parsers to set the Device Tree node for a partition by +introducing of_put_partition() and extending struct parsed_partitions +accordingly. + +As the partition information is preallocated independently of the actual +number of partitions the additional pointer takes about 2 kiB of allocated +memory which is worth avoiding in case CONFIG_OF is not set. This is +achieved by only adding the corresponding field to the struct in case +CONFIG_OF is set using #ifdef'ery. + +Signed-off-by: Daniel Golle +--- + block/partitions/check.h | 16 +++++++++++++++- + block/partitions/core.c | 14 +++++++++++--- + 2 files changed, 26 insertions(+), 4 deletions(-) + +--- a/block/partitions/check.h ++++ b/block/partitions/check.h +@@ -1,6 +1,7 @@ + /* SPDX-License-Identifier: GPL-2.0 */ + #include + #include ++#include + #include "../blk.h" + + /* +@@ -16,6 +17,9 @@ struct parsed_partitions { + int flags; + bool has_info; + struct partition_meta_info info; ++#ifdef CONFIG_OF ++ struct device_node *np; ++#endif + } *parts; + int next; + int limit; +@@ -34,18 +38,28 @@ static inline void put_dev_sector(Sector + } + + static inline void +-put_partition(struct parsed_partitions *p, int n, sector_t from, sector_t size) ++of_put_partition(struct parsed_partitions *p, int n, sector_t from, sector_t size, ++ struct device_node *np) + { + if (n < p->limit) { + char tmp[1 + BDEVNAME_SIZE + 10 + 1]; + + p->parts[n].from = from; + p->parts[n].size = size; ++#ifdef CONFIG_OF ++ p->parts[n].np = np; ++#endif + snprintf(tmp, sizeof(tmp), " %s%d", p->name, n); + strlcat(p->pp_buf, tmp, PAGE_SIZE); + } + } + ++static inline void ++put_partition(struct parsed_partitions *p, int n, sector_t from, sector_t size) ++{ ++ of_put_partition(p, n, from, size, NULL); ++} ++ + /* detection routines go here in alphabetical order: */ + int adfspart_check_ADFS(struct parsed_partitions *state); + int adfspart_check_CUMANA(struct parsed_partitions *state); +--- a/block/partitions/core.c ++++ b/block/partitions/core.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include "check.h" + +@@ -290,7 +291,8 @@ static const DEVICE_ATTR(whole_disk, 044 + */ + static struct block_device *add_partition(struct gendisk *disk, int partno, + sector_t start, sector_t len, int flags, +- struct partition_meta_info *info) ++ struct partition_meta_info *info, ++ struct device_node *np) + { + dev_t devt = MKDEV(0, 0); + struct device *ddev = disk_to_dev(disk); +@@ -339,6 +341,7 @@ static struct block_device *add_partitio + pdev->class = &block_class; + pdev->type = &part_type; + pdev->parent = ddev; ++ device_set_node(pdev, of_fwnode_handle(np)); + + /* in consecutive minor range? */ + if (bdev_partno(bdev) < disk->minors) { +@@ -445,7 +448,7 @@ int bdev_add_partition(struct gendisk *d + } + + part = add_partition(disk, partno, start, length, +- ADDPART_FLAG_NONE, NULL); ++ ADDPART_FLAG_NONE, NULL, NULL); + ret = PTR_ERR_OR_ZERO(part); + out: + mutex_unlock(&disk->open_mutex); +@@ -559,8 +562,13 @@ static bool blk_add_partition(struct gen + size = get_capacity(disk) - from; + } + ++#ifdef CONFIG_OF + part = add_partition(disk, p, from, size, state->parts[p].flags, +- &state->parts[p].info); ++ &state->parts[p].info, state->parts[p].np); ++#else ++ part = add_partition(disk, p, from, size, state->parts[p].flags, ++ &state->parts[p].info, NULL); ++#endif + if (IS_ERR(part)) { + if (PTR_ERR(part) != -ENXIO) { + printk(KERN_ERR " %s: p%d could not be added: %pe\n", diff --git a/target/linux/generic/pending-6.12/451-block-partitions-of-assign-Device-Tree-node-to-parti.patch b/target/linux/generic/pending-6.12/451-block-partitions-of-assign-Device-Tree-node-to-parti.patch new file mode 100644 index 00000000000..f9e41d03c85 --- /dev/null +++ b/target/linux/generic/pending-6.12/451-block-partitions-of-assign-Device-Tree-node-to-parti.patch @@ -0,0 +1,25 @@ +From 5d265996597a6b0d654bbeda1bc3bae197061de7 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Oct 2024 23:43:32 +0100 +Subject: [PATCH 2/8] block: partitions: of: assign Device Tree node to + partition + +Assign partition of_node so other drivers are able to identify a +partition by its Device Tree node. + +Signed-off-by: Daniel Golle +--- + block/partitions/of.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/block/partitions/of.c ++++ b/block/partitions/of.c +@@ -48,7 +48,7 @@ static void add_of_partition(struct pars + u64 offset = of_read_number(reg, a_cells) / SECTOR_SIZE; + u64 size = of_read_number(reg + a_cells, s_cells) / SECTOR_SIZE; + +- put_partition(state, slot, offset, size); ++ of_put_partition(state, slot, offset, size, np); + + if (of_property_read_bool(np, "read-only")) + state->parts[slot].flags |= ADDPART_FLAG_READONLY; diff --git a/target/linux/generic/pending-6.12/452-partitions-efi-apply-Linux-code-style.patch b/target/linux/generic/pending-6.12/452-partitions-efi-apply-Linux-code-style.patch new file mode 100644 index 00000000000..6e996ca3f70 --- /dev/null +++ b/target/linux/generic/pending-6.12/452-partitions-efi-apply-Linux-code-style.patch @@ -0,0 +1,498 @@ +From d4e82837c8b86ff2c21fa923271908988bc72faa Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Oct 2024 23:22:53 +0100 +Subject: [PATCH 3/8] partitions/efi: apply Linux code style + +Fix (mostly white space related) coding style issues in +block/partitions/efi.c by use of clang-format. + +Signed-off-by: Daniel Golle +--- + block/partitions/efi.c | 235 +++++++++++++++++++++-------------------- + 1 file changed, 121 insertions(+), 114 deletions(-) + +--- a/block/partitions/efi.c ++++ b/block/partitions/efi.c +@@ -95,15 +95,13 @@ + * the partition tables happens after init too. + */ + static int force_gpt; +-static int __init +-force_gpt_fn(char *str) ++static int __init force_gpt_fn(char *str) + { + force_gpt = 1; + return 1; + } + __setup("gpt", force_gpt_fn); + +- + /** + * efi_crc32() - EFI version of crc32 function + * @buf: buffer to calculate crc32 of +@@ -116,8 +114,7 @@ __setup("gpt", force_gpt_fn); + * Note, the EFI Specification, v1.02, has a reference to + * Dr. Dobbs Journal, May 1994 (actually it's in May 1992). + */ +-static inline u32 +-efi_crc32(const void *buf, unsigned long len) ++static inline u32 efi_crc32(const void *buf, unsigned long len) + { + return (crc32(~0L, buf, len) ^ ~0L); + } +@@ -134,7 +131,8 @@ efi_crc32(const void *buf, unsigned long + static u64 last_lba(struct gendisk *disk) + { + return div_u64(bdev_nr_bytes(disk->part0), +- queue_logical_block_size(disk->queue)) - 1ULL; ++ queue_logical_block_size(disk->queue)) - ++ 1ULL; + } + + static inline int pmbr_part_valid(gpt_mbr_record *part) +@@ -195,7 +193,7 @@ static int is_pmbr_valid(legacy_mbr *mbr + check_hybrid: + for (i = 0; i < 4; i++) + if ((mbr->partition_record[i].os_type != +- EFI_PMBR_OSTYPE_EFI_GPT) && ++ EFI_PMBR_OSTYPE_EFI_GPT) && + (mbr->partition_record[i].os_type != 0x00)) + ret = GPT_MBR_HYBRID; + +@@ -213,10 +211,11 @@ check_hybrid: + */ + if (ret == GPT_MBR_PROTECTIVE) { + sz = le32_to_cpu(mbr->partition_record[part].size_in_lba); +- if (sz != (uint32_t) total_sectors - 1 && sz != 0xFFFFFFFF) +- pr_debug("GPT: mbr size in lba (%u) different than whole disk (%u).\n", +- sz, min_t(uint32_t, +- total_sectors - 1, 0xFFFFFFFF)); ++ if (sz != (uint32_t)total_sectors - 1 && sz != 0xFFFFFFFF) ++ pr_debug( ++ "GPT: mbr size in lba (%u) different than whole disk (%u).\n", ++ sz, ++ min_t(uint32_t, total_sectors - 1, 0xFFFFFFFF)); + } + done: + return ret; +@@ -232,15 +231,14 @@ done: + * Description: Reads @count bytes from @state->disk into @buffer. + * Returns number of bytes read on success, 0 on error. + */ +-static size_t read_lba(struct parsed_partitions *state, +- u64 lba, u8 *buffer, size_t count) ++static size_t read_lba(struct parsed_partitions *state, u64 lba, u8 *buffer, ++ size_t count) + { + size_t totalreadcount = 0; +- sector_t n = lba * +- (queue_logical_block_size(state->disk->queue) / 512); ++ sector_t n = lba * (queue_logical_block_size(state->disk->queue) / 512); + + if (!buffer || lba > last_lba(state->disk)) +- return 0; ++ return 0; + + while (count) { + int copied = 512; +@@ -253,7 +251,7 @@ static size_t read_lba(struct parsed_par + memcpy(buffer, data, copied); + put_dev_sector(sect); + buffer += copied; +- totalreadcount +=copied; ++ totalreadcount += copied; + count -= copied; + } + return totalreadcount; +@@ -278,17 +276,17 @@ static gpt_entry *alloc_read_gpt_entries + return NULL; + + count = (size_t)le32_to_cpu(gpt->num_partition_entries) * +- le32_to_cpu(gpt->sizeof_partition_entry); ++ le32_to_cpu(gpt->sizeof_partition_entry); + if (!count) + return NULL; + pte = kmalloc(count, GFP_KERNEL); + if (!pte) + return NULL; + +- if (read_lba(state, le64_to_cpu(gpt->partition_entry_lba), +- (u8 *) pte, count) < count) { ++ if (read_lba(state, le64_to_cpu(gpt->partition_entry_lba), (u8 *)pte, ++ count) < count) { + kfree(pte); +- pte=NULL; ++ pte = NULL; + return NULL; + } + return pte; +@@ -313,9 +311,9 @@ static gpt_header *alloc_read_gpt_header + if (!gpt) + return NULL; + +- if (read_lba(state, lba, (u8 *) gpt, ssz) < ssz) { ++ if (read_lba(state, lba, (u8 *)gpt, ssz) < ssz) { + kfree(gpt); +- gpt=NULL; ++ gpt = NULL; + return NULL; + } + +@@ -354,8 +352,9 @@ static int is_gpt_valid(struct parsed_pa + + /* Check the GUID Partition Table header size is too big */ + if (le32_to_cpu((*gpt)->header_size) > +- queue_logical_block_size(state->disk->queue)) { +- pr_debug("GUID Partition Table Header size is too large: %u > %u\n", ++ queue_logical_block_size(state->disk->queue)) { ++ pr_debug( ++ "GUID Partition Table Header size is too large: %u > %u\n", + le32_to_cpu((*gpt)->header_size), + queue_logical_block_size(state->disk->queue)); + goto fail; +@@ -363,16 +362,17 @@ static int is_gpt_valid(struct parsed_pa + + /* Check the GUID Partition Table header size is too small */ + if (le32_to_cpu((*gpt)->header_size) < sizeof(gpt_header)) { +- pr_debug("GUID Partition Table Header size is too small: %u < %zu\n", +- le32_to_cpu((*gpt)->header_size), +- sizeof(gpt_header)); ++ pr_debug( ++ "GUID Partition Table Header size is too small: %u < %zu\n", ++ le32_to_cpu((*gpt)->header_size), sizeof(gpt_header)); + goto fail; + } + + /* Check the GUID Partition Table CRC */ + origcrc = le32_to_cpu((*gpt)->header_crc32); + (*gpt)->header_crc32 = 0; +- crc = efi_crc32((const unsigned char *) (*gpt), le32_to_cpu((*gpt)->header_size)); ++ crc = efi_crc32((const unsigned char *)(*gpt), ++ le32_to_cpu((*gpt)->header_size)); + + if (crc != origcrc) { + pr_debug("GUID Partition Table Header CRC is wrong: %x != %x\n", +@@ -396,20 +396,25 @@ static int is_gpt_valid(struct parsed_pa + lastlba = last_lba(state->disk); + if (le64_to_cpu((*gpt)->first_usable_lba) > lastlba) { + pr_debug("GPT: first_usable_lba incorrect: %lld > %lld\n", +- (unsigned long long)le64_to_cpu((*gpt)->first_usable_lba), ++ (unsigned long long)le64_to_cpu( ++ (*gpt)->first_usable_lba), + (unsigned long long)lastlba); + goto fail; + } + if (le64_to_cpu((*gpt)->last_usable_lba) > lastlba) { + pr_debug("GPT: last_usable_lba incorrect: %lld > %lld\n", +- (unsigned long long)le64_to_cpu((*gpt)->last_usable_lba), ++ (unsigned long long)le64_to_cpu( ++ (*gpt)->last_usable_lba), + (unsigned long long)lastlba); + goto fail; + } +- if (le64_to_cpu((*gpt)->last_usable_lba) < le64_to_cpu((*gpt)->first_usable_lba)) { ++ if (le64_to_cpu((*gpt)->last_usable_lba) < ++ le64_to_cpu((*gpt)->first_usable_lba)) { + pr_debug("GPT: last_usable_lba incorrect: %lld > %lld\n", +- (unsigned long long)le64_to_cpu((*gpt)->last_usable_lba), +- (unsigned long long)le64_to_cpu((*gpt)->first_usable_lba)); ++ (unsigned long long)le64_to_cpu( ++ (*gpt)->last_usable_lba), ++ (unsigned long long)le64_to_cpu( ++ (*gpt)->first_usable_lba)); + goto fail; + } + /* Check that sizeof_partition_entry has the correct value */ +@@ -420,10 +425,11 @@ static int is_gpt_valid(struct parsed_pa + + /* Sanity check partition table size */ + pt_size = (u64)le32_to_cpu((*gpt)->num_partition_entries) * +- le32_to_cpu((*gpt)->sizeof_partition_entry); ++ le32_to_cpu((*gpt)->sizeof_partition_entry); + if (pt_size > KMALLOC_MAX_SIZE) { +- pr_debug("GUID Partition Table is too large: %llu > %lu bytes\n", +- (unsigned long long)pt_size, KMALLOC_MAX_SIZE); ++ pr_debug( ++ "GUID Partition Table is too large: %llu > %lu bytes\n", ++ (unsigned long long)pt_size, KMALLOC_MAX_SIZE); + goto fail; + } + +@@ -431,7 +437,7 @@ static int is_gpt_valid(struct parsed_pa + goto fail; + + /* Check the GUID Partition Entry Array CRC */ +- crc = efi_crc32((const unsigned char *) (*ptes), pt_size); ++ crc = efi_crc32((const unsigned char *)(*ptes), pt_size); + + if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) { + pr_debug("GUID Partition Entry Array CRC check failed.\n"); +@@ -441,10 +447,10 @@ static int is_gpt_valid(struct parsed_pa + /* We're done, all's well */ + return 1; + +- fail_ptes: ++fail_ptes: + kfree(*ptes); + *ptes = NULL; +- fail: ++fail: + kfree(*gpt); + *gpt = NULL; + return 0; +@@ -457,12 +463,11 @@ static int is_gpt_valid(struct parsed_pa + * + * Description: returns 1 if valid, 0 on error. + */ +-static inline int +-is_pte_valid(const gpt_entry *pte, const u64 lastlba) ++static inline int is_pte_valid(const gpt_entry *pte, const u64 lastlba) + { + if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) || +- le64_to_cpu(pte->starting_lba) > lastlba || +- le64_to_cpu(pte->ending_lba) > lastlba) ++ le64_to_cpu(pte->starting_lba) > lastlba || ++ le64_to_cpu(pte->ending_lba) > lastlba) + return 0; + return 1; + } +@@ -477,8 +482,7 @@ is_pte_valid(const gpt_entry *pte, const + * and prints warnings on discrepancies. + * + */ +-static void +-compare_gpts(gpt_header *pgpt, gpt_header *agpt, u64 lastlba) ++static void compare_gpts(gpt_header *pgpt, gpt_header *agpt, u64 lastlba) + { + int error_found = 0; + if (!pgpt || !agpt) +@@ -486,31 +490,32 @@ compare_gpts(gpt_header *pgpt, gpt_heade + if (le64_to_cpu(pgpt->my_lba) != le64_to_cpu(agpt->alternate_lba)) { + pr_warn("GPT:Primary header LBA != Alt. header alternate_lba\n"); + pr_warn("GPT:%lld != %lld\n", +- (unsigned long long)le64_to_cpu(pgpt->my_lba), +- (unsigned long long)le64_to_cpu(agpt->alternate_lba)); ++ (unsigned long long)le64_to_cpu(pgpt->my_lba), ++ (unsigned long long)le64_to_cpu(agpt->alternate_lba)); + error_found++; + } + if (le64_to_cpu(pgpt->alternate_lba) != le64_to_cpu(agpt->my_lba)) { + pr_warn("GPT:Primary header alternate_lba != Alt. header my_lba\n"); + pr_warn("GPT:%lld != %lld\n", +- (unsigned long long)le64_to_cpu(pgpt->alternate_lba), +- (unsigned long long)le64_to_cpu(agpt->my_lba)); ++ (unsigned long long)le64_to_cpu(pgpt->alternate_lba), ++ (unsigned long long)le64_to_cpu(agpt->my_lba)); + error_found++; + } + if (le64_to_cpu(pgpt->first_usable_lba) != +- le64_to_cpu(agpt->first_usable_lba)) { ++ le64_to_cpu(agpt->first_usable_lba)) { + pr_warn("GPT:first_usable_lbas don't match.\n"); + pr_warn("GPT:%lld != %lld\n", +- (unsigned long long)le64_to_cpu(pgpt->first_usable_lba), +- (unsigned long long)le64_to_cpu(agpt->first_usable_lba)); ++ (unsigned long long)le64_to_cpu(pgpt->first_usable_lba), ++ (unsigned long long)le64_to_cpu( ++ agpt->first_usable_lba)); + error_found++; + } + if (le64_to_cpu(pgpt->last_usable_lba) != +- le64_to_cpu(agpt->last_usable_lba)) { ++ le64_to_cpu(agpt->last_usable_lba)) { + pr_warn("GPT:last_usable_lbas don't match.\n"); + pr_warn("GPT:%lld != %lld\n", +- (unsigned long long)le64_to_cpu(pgpt->last_usable_lba), +- (unsigned long long)le64_to_cpu(agpt->last_usable_lba)); ++ (unsigned long long)le64_to_cpu(pgpt->last_usable_lba), ++ (unsigned long long)le64_to_cpu(agpt->last_usable_lba)); + error_found++; + } + if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) { +@@ -518,27 +523,27 @@ compare_gpts(gpt_header *pgpt, gpt_heade + error_found++; + } + if (le32_to_cpu(pgpt->num_partition_entries) != +- le32_to_cpu(agpt->num_partition_entries)) { ++ le32_to_cpu(agpt->num_partition_entries)) { + pr_warn("GPT:num_partition_entries don't match: " +- "0x%x != 0x%x\n", +- le32_to_cpu(pgpt->num_partition_entries), +- le32_to_cpu(agpt->num_partition_entries)); ++ "0x%x != 0x%x\n", ++ le32_to_cpu(pgpt->num_partition_entries), ++ le32_to_cpu(agpt->num_partition_entries)); + error_found++; + } + if (le32_to_cpu(pgpt->sizeof_partition_entry) != +- le32_to_cpu(agpt->sizeof_partition_entry)) { ++ le32_to_cpu(agpt->sizeof_partition_entry)) { + pr_warn("GPT:sizeof_partition_entry values don't match: " +- "0x%x != 0x%x\n", +- le32_to_cpu(pgpt->sizeof_partition_entry), +- le32_to_cpu(agpt->sizeof_partition_entry)); ++ "0x%x != 0x%x\n", ++ le32_to_cpu(pgpt->sizeof_partition_entry), ++ le32_to_cpu(agpt->sizeof_partition_entry)); + error_found++; + } + if (le32_to_cpu(pgpt->partition_entry_array_crc32) != +- le32_to_cpu(agpt->partition_entry_array_crc32)) { ++ le32_to_cpu(agpt->partition_entry_array_crc32)) { + pr_warn("GPT:partition_entry_array_crc32 values don't match: " +- "0x%x != 0x%x\n", +- le32_to_cpu(pgpt->partition_entry_array_crc32), +- le32_to_cpu(agpt->partition_entry_array_crc32)); ++ "0x%x != 0x%x\n", ++ le32_to_cpu(pgpt->partition_entry_array_crc32), ++ le32_to_cpu(agpt->partition_entry_array_crc32)); + error_found++; + } + if (le64_to_cpu(pgpt->alternate_lba) != lastlba) { +@@ -594,7 +599,7 @@ static int find_valid_gpt(struct parsed_ + return 0; + + lastlba = last_lba(state->disk); +- if (!force_gpt) { ++ if (!force_gpt) { + /* This will be added to the EFI Spec. per Intel after v1.02. */ + legacymbr = kzalloc(sizeof(*legacymbr), GFP_KERNEL); + if (!legacymbr) +@@ -608,18 +613,17 @@ static int find_valid_gpt(struct parsed_ + goto fail; + + pr_debug("Device has a %s MBR\n", +- good_pmbr == GPT_MBR_PROTECTIVE ? +- "protective" : "hybrid"); ++ good_pmbr == GPT_MBR_PROTECTIVE ? "protective" : ++ "hybrid"); + } + +- good_pgpt = is_gpt_valid(state, GPT_PRIMARY_PARTITION_TABLE_LBA, +- &pgpt, &pptes); +- if (good_pgpt) +- good_agpt = is_gpt_valid(state, +- le64_to_cpu(pgpt->alternate_lba), +- &agpt, &aptes); +- if (!good_agpt && force_gpt) +- good_agpt = is_gpt_valid(state, lastlba, &agpt, &aptes); ++ good_pgpt = is_gpt_valid(state, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, ++ &pptes); ++ if (good_pgpt) ++ good_agpt = is_gpt_valid( ++ state, le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes); ++ if (!good_agpt && force_gpt) ++ good_agpt = is_gpt_valid(state, lastlba, &agpt, &aptes); + + if (!good_agpt && force_gpt && fops->alternative_gpt_sector) { + sector_t agpt_sector; +@@ -627,43 +631,42 @@ static int find_valid_gpt(struct parsed_ + + err = fops->alternative_gpt_sector(disk, &agpt_sector); + if (!err) +- good_agpt = is_gpt_valid(state, agpt_sector, +- &agpt, &aptes); ++ good_agpt = ++ is_gpt_valid(state, agpt_sector, &agpt, &aptes); + } + +- /* The obviously unsuccessful case */ +- if (!good_pgpt && !good_agpt) +- goto fail; +- +- compare_gpts(pgpt, agpt, lastlba); +- +- /* The good cases */ +- if (good_pgpt) { +- *gpt = pgpt; +- *ptes = pptes; +- kfree(agpt); +- kfree(aptes); ++ /* The obviously unsuccessful case */ ++ if (!good_pgpt && !good_agpt) ++ goto fail; ++ ++ compare_gpts(pgpt, agpt, lastlba); ++ ++ /* The good cases */ ++ if (good_pgpt) { ++ *gpt = pgpt; ++ *ptes = pptes; ++ kfree(agpt); ++ kfree(aptes); + if (!good_agpt) +- pr_warn("Alternate GPT is invalid, using primary GPT.\n"); +- return 1; +- } +- else if (good_agpt) { +- *gpt = agpt; +- *ptes = aptes; +- kfree(pgpt); +- kfree(pptes); ++ pr_warn("Alternate GPT is invalid, using primary GPT.\n"); ++ return 1; ++ } else if (good_agpt) { ++ *gpt = agpt; ++ *ptes = aptes; ++ kfree(pgpt); ++ kfree(pptes); + pr_warn("Primary GPT is invalid, using alternate GPT.\n"); +- return 1; +- } ++ return 1; ++ } + +- fail: +- kfree(pgpt); +- kfree(agpt); +- kfree(pptes); +- kfree(aptes); +- *gpt = NULL; +- *ptes = NULL; +- return 0; ++fail: ++ kfree(pgpt); ++ kfree(agpt); ++ kfree(pptes); ++ kfree(aptes); ++ *gpt = NULL; ++ *ptes = NULL; ++ return 0; + } + + /** +@@ -725,7 +728,9 @@ int efi_partition(struct parsed_partitio + + pr_debug("GUID Partition Table is valid! Yea!\n"); + +- for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < state->limit-1; i++) { ++ for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && ++ i < state->limit - 1; ++ i++) { + struct partition_meta_info *info; + unsigned label_max; + u64 start = le64_to_cpu(ptes[i].starting_lba); +@@ -735,10 +740,11 @@ int efi_partition(struct parsed_partitio + if (!is_pte_valid(&ptes[i], last_lba(state->disk))) + continue; + +- put_partition(state, i+1, start * ssz, size * ssz); ++ put_partition(state, i + 1, start * ssz, size * ssz); + + /* If this is a RAID volume, tell md */ +- if (!efi_guidcmp(ptes[i].partition_type_guid, PARTITION_LINUX_RAID_GUID)) ++ if (!efi_guidcmp(ptes[i].partition_type_guid, ++ PARTITION_LINUX_RAID_GUID)) + state->parts[i + 1].flags = ADDPART_FLAG_RAID; + + info = &state->parts[i + 1].info; +@@ -747,7 +753,8 @@ int efi_partition(struct parsed_partitio + /* Naively convert UTF16-LE to 7 bits. */ + label_max = min(ARRAY_SIZE(info->volname) - 1, + ARRAY_SIZE(ptes[i].partition_name)); +- utf16_le_to_7bit(ptes[i].partition_name, label_max, info->volname); ++ utf16_le_to_7bit(ptes[i].partition_name, label_max, ++ info->volname); + state->parts[i + 1].has_info = true; + } + kfree(ptes); diff --git a/target/linux/generic/pending-6.12/453-partitions-efi-allow-assigning-partition-Device-Tree.patch b/target/linux/generic/pending-6.12/453-partitions-efi-allow-assigning-partition-Device-Tree.patch new file mode 100644 index 00000000000..cc2c20338af --- /dev/null +++ b/target/linux/generic/pending-6.12/453-partitions-efi-allow-assigning-partition-Device-Tree.patch @@ -0,0 +1,75 @@ +From f28e36c19b927671d93ccd2b5de3d518fccf721e Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 8 Oct 2024 00:25:12 +0100 +Subject: [PATCH 4/8] partitions/efi: allow assigning partition Device Tree + node + +Signed-off-by: Daniel Golle +--- + block/partitions/efi.c | 34 +++++++++++++++++++++++++++++++++- + 1 file changed, 33 insertions(+), 1 deletion(-) + +--- a/block/partitions/efi.c ++++ b/block/partitions/efi.c +@@ -86,6 +86,7 @@ + #include + #include + #include ++#include + #include + #include "check.h" + #include "efi.h" +@@ -694,6 +695,34 @@ static void utf16_le_to_7bit(const __le1 + } + } + ++static struct device_node *find_partition_of_node(struct device_node *partitions_np, ++ gpt_entry *ptes) ++{ ++ char volname[64], partuuid[UUID_STRING_LEN + 1]; ++ const char *uuid, *name; ++ ++ if (!partitions_np || ++ !of_device_is_compatible(partitions_np, "gpt-partitions")) ++ return NULL; ++ ++ efi_guid_to_str(&ptes->unique_partition_guid, partuuid); ++ utf16_le_to_7bit(ptes->partition_name, ARRAY_SIZE(volname) - 1, volname); ++ ++ for_each_available_child_of_node_scoped(partitions_np, np) { ++ if (!of_property_read_string(np, "uuid", &uuid) && ++ strncmp(uuid, partuuid, ARRAY_SIZE(partuuid))) ++ continue; ++ ++ if (!of_property_read_string(np, "partname", &name) && ++ strncmp(name, volname, ARRAY_SIZE(volname))) ++ continue; ++ ++ return np; ++ } ++ ++ return NULL; ++} ++ + /** + * efi_partition - scan for GPT partitions + * @state: disk parsed partitions +@@ -719,6 +748,8 @@ int efi_partition(struct parsed_partitio + gpt_entry *ptes = NULL; + u32 i; + unsigned ssz = queue_logical_block_size(state->disk->queue) / 512; ++ struct device *ddev = disk_to_dev(state->disk); ++ struct device_node *partitions_np = of_node_get(ddev->of_node); + + if (!find_valid_gpt(state, &gpt, &ptes) || !gpt || !ptes) { + kfree(gpt); +@@ -740,7 +771,8 @@ int efi_partition(struct parsed_partitio + if (!is_pte_valid(&ptes[i], last_lba(state->disk))) + continue; + +- put_partition(state, i + 1, start * ssz, size * ssz); ++ of_put_partition(state, i + 1, start * ssz, size * ssz, ++ find_partition_of_node(partitions_np, &ptes[i])); + + /* If this is a RAID volume, tell md */ + if (!efi_guidcmp(ptes[i].partition_type_guid, diff --git a/target/linux/generic/pending-6.12/454-block-add-support-for-notifications.patch b/target/linux/generic/pending-6.12/454-block-add-support-for-notifications.patch new file mode 100644 index 00000000000..6a5abef682f --- /dev/null +++ b/target/linux/generic/pending-6.12/454-block-add-support-for-notifications.patch @@ -0,0 +1,164 @@ +From 858df9db80c1568db255659baca7602504f823af Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 15 May 2024 08:19:51 +0100 +Subject: [PATCH 5/8] block: add support for notifications + +Add notifier block to notify other subsystems about the addition or +removal of block devices. + +Signed-off-by: Daniel Golle +--- + block/Kconfig | 6 +++ + block/Makefile | 1 + + block/blk-notify.c | 107 +++++++++++++++++++++++++++++++++++++++++ + include/linux/blkdev.h | 8 +++ + 4 files changed, 122 insertions(+) + create mode 100644 block/blk-notify.c + +--- a/block/Kconfig ++++ b/block/Kconfig +@@ -209,6 +209,12 @@ config BLK_INLINE_ENCRYPTION_FALLBACK + by falling back to the kernel crypto API when inline + encryption hardware is not present. + ++config BLOCK_NOTIFIERS ++ bool "Enable support for notifications in block layer" ++ help ++ Enable this option to provide notifiers for other subsystems ++ upon addition or removal of block devices. ++ + source "block/partitions/Kconfig" + + config BLK_MQ_PCI +--- a/block/Makefile ++++ b/block/Makefile +@@ -38,3 +38,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += b + blk-crypto-sysfs.o + obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o + obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) += holder.o ++obj-$(CONFIG_BLOCK_NOTIFIERS) += blk-notify.o +--- /dev/null ++++ b/block/blk-notify.c +@@ -0,0 +1,107 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Notifiers for addition and removal of block devices ++ * ++ * Copyright (c) 2024 Daniel Golle ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "blk.h" ++ ++struct blk_device_list { ++ struct device *dev; ++ struct list_head list; ++}; ++ ++static RAW_NOTIFIER_HEAD(blk_notifier_list); ++static DEFINE_MUTEX(blk_notifier_lock); ++static LIST_HEAD(blk_devices); ++ ++struct blk_notify_event { ++ struct delayed_work work; ++ struct device *dev; ++}; ++ ++void blk_register_notify(struct notifier_block *nb) ++{ ++ struct blk_device_list *existing_blkdev; ++ ++ mutex_lock(&blk_notifier_lock); ++ raw_notifier_chain_register(&blk_notifier_list, nb); ++ ++ list_for_each_entry(existing_blkdev, &blk_devices, list) ++ nb->notifier_call(nb, BLK_DEVICE_ADD, existing_blkdev->dev); ++ ++ mutex_unlock(&blk_notifier_lock); ++} ++EXPORT_SYMBOL_GPL(blk_register_notify); ++ ++void blk_unregister_notify(struct notifier_block *nb) ++{ ++ mutex_lock(&blk_notifier_lock); ++ raw_notifier_chain_unregister(&blk_notifier_list, nb); ++ mutex_unlock(&blk_notifier_lock); ++} ++EXPORT_SYMBOL_GPL(blk_unregister_notify); ++ ++static void blk_notify_work(struct work_struct *work) ++{ ++ struct blk_notify_event *ev = ++ container_of(work, struct blk_notify_event, work.work); ++ ++ raw_notifier_call_chain(&blk_notifier_list, BLK_DEVICE_ADD, ev->dev); ++ kfree(ev); ++} ++ ++static int blk_call_notifier_add(struct device *dev) ++{ ++ struct blk_device_list *new_blkdev; ++ struct blk_notify_event *ev; ++ ++ new_blkdev = kmalloc(sizeof (*new_blkdev), GFP_KERNEL); ++ if (!new_blkdev) ++ return -ENOMEM; ++ ++ ev = kmalloc(sizeof(*ev), GFP_KERNEL); ++ INIT_DEFERRABLE_WORK(&ev->work, blk_notify_work); ++ ev->dev = dev; ++ new_blkdev->dev = dev; ++ mutex_lock(&blk_notifier_lock); ++ list_add_tail(&new_blkdev->list, &blk_devices); ++ schedule_delayed_work(&ev->work, msecs_to_jiffies(500)); ++ mutex_unlock(&blk_notifier_lock); ++ ++ return 0; ++} ++ ++static void blk_call_notifier_remove(struct device *dev) ++{ ++ struct blk_device_list *old_blkdev, *tmp; ++ ++ mutex_lock(&blk_notifier_lock); ++ list_for_each_entry_safe(old_blkdev, tmp, &blk_devices, list) { ++ if (old_blkdev->dev != dev) ++ continue; ++ ++ list_del(&old_blkdev->list); ++ kfree(old_blkdev); ++ } ++ raw_notifier_call_chain(&blk_notifier_list, BLK_DEVICE_REMOVE, dev); ++ mutex_unlock(&blk_notifier_lock); ++} ++ ++static struct class_interface blk_notifications_bus_interface __refdata = { ++ .class = &block_class, ++ .add_dev = &blk_call_notifier_add, ++ .remove_dev = &blk_call_notifier_remove, ++}; ++ ++static int __init blk_notifications_init(void) ++{ ++ return class_interface_register(&blk_notifications_bus_interface); ++} ++device_initcall(blk_notifications_init); +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -1753,4 +1753,12 @@ static inline bool bdev_can_atomic_write + + #define DEFINE_IO_COMP_BATCH(name) struct io_comp_batch name = { } + ++ ++#ifdef CONFIG_BLOCK_NOTIFIERS ++#define BLK_DEVICE_ADD 1 ++#define BLK_DEVICE_REMOVE 2 ++extern void blk_register_notify(struct notifier_block *nb); ++extern void blk_unregister_notify(struct notifier_block *nb); ++#endif ++ + #endif /* _LINUX_BLKDEV_H */ diff --git a/target/linux/generic/pending-6.12/455-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch b/target/linux/generic/pending-6.12/455-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch new file mode 100644 index 00000000000..4f9a26fd161 --- /dev/null +++ b/target/linux/generic/pending-6.12/455-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch @@ -0,0 +1,29 @@ +From 2b28bf485b283991eea9f00c5831c1c39c73991f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 21 Mar 2024 19:33:49 +0000 +Subject: [PATCH 6/8] block: add new genhd flag GENHD_FL_NVMEM + +Add new flag to destinguish block devices which may act as an NVMEM +provider. + +Signed-off-by: Daniel Golle +--- + include/linux/blkdev.h | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -82,11 +82,13 @@ struct partition_meta_info { + * ``GENHD_FL_NO_PART``: partition support is disabled. The kernel will not + * scan for partitions from add_disk, and users can't add partitions manually. + * ++ * ``GENHD_FL_NVMEM``: the block device should be considered as NVMEM provider. + */ + enum { + GENHD_FL_REMOVABLE = 1 << 0, + GENHD_FL_HIDDEN = 1 << 1, + GENHD_FL_NO_PART = 1 << 2, ++ GENHD_FL_NVMEM = 1 << 3, + }; + + enum { diff --git a/target/linux/generic/pending-6.12/456-nvmem-implement-block-NVMEM-provider.patch b/target/linux/generic/pending-6.12/456-nvmem-implement-block-NVMEM-provider.patch new file mode 100644 index 00000000000..563b9f1259d --- /dev/null +++ b/target/linux/generic/pending-6.12/456-nvmem-implement-block-NVMEM-provider.patch @@ -0,0 +1,250 @@ +From 26ae98bf77fdbd60536fe5c2175e528469b9ac9a Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 15 May 2024 18:18:37 +0300 +Subject: [PATCH 7/8] nvmem: implement block NVMEM provider + +On embedded devices using an eMMC it is common that one or more partitions +on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM +data. Allow referencing any block device or partition in Device Tree to +allow e.g. Ethernet and Wi-Fi drivers accessing them via the NVMEM layer. + +Signed-off-by: Daniel Golle +--- + drivers/nvmem/Kconfig | 11 +++ + drivers/nvmem/Makefile | 2 + + drivers/nvmem/block.c | 198 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 211 insertions(+) + create mode 100644 drivers/nvmem/block.c + +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -40,6 +40,17 @@ config NVMEM_APPLE_EFUSES + This driver can also be built as a module. If so, the module will + be called nvmem-apple-efuses. + ++config NVMEM_BLOCK ++ tristate "Block device NVMEM provider" ++ depends on BLOCK ++ depends on OF ++ depends on NVMEM ++ select BLOCK_NOTIFIERS ++ help ++ Allow block devices (or partitions) to act as NVMEM prodivers, ++ typically used with eMMC to store MAC addresses or Wi-Fi ++ calibration data on embedded devices. ++ + config NVMEM_BCM_OCOTP + tristate "Broadcom On-Chip OTP Controller support" + depends on ARCH_BCM_IPROC || COMPILE_TEST +--- a/drivers/nvmem/Makefile ++++ b/drivers/nvmem/Makefile +@@ -14,6 +14,8 @@ obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvme + nvmem-apple-efuses-y := apple-efuses.o + obj-$(CONFIG_NVMEM_BCM_OCOTP) += nvmem-bcm-ocotp.o + nvmem-bcm-ocotp-y := bcm-ocotp.o ++obj-$(CONFIG_NVMEM_BLOCK) += nvmem-block.o ++nvmem-block-y := block.o + obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o + nvmem_brcm_nvram-y := brcm_nvram.o + obj-$(CONFIG_NVMEM_IMX_IIM) += nvmem-imx-iim.o +--- /dev/null ++++ b/drivers/nvmem/block.c +@@ -0,0 +1,198 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * block device NVMEM provider ++ * ++ * Copyright (c) 2024 Daniel Golle ++ * ++ * Useful on devices using a partition on an eMMC for MAC addresses or ++ * Wi-Fi calibration EEPROM data. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* List of all NVMEM devices */ ++static LIST_HEAD(nvmem_devices); ++static DEFINE_MUTEX(devices_mutex); ++ ++struct blk_nvmem { ++ struct nvmem_device *nvmem; ++ struct device *dev; ++ struct list_head list; ++}; ++ ++static int blk_nvmem_reg_read(void *priv, unsigned int from, ++ void *val, size_t bytes) ++{ ++ blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES; ++ unsigned long offs = from & ~PAGE_MASK, to_read; ++ pgoff_t f_index = from >> PAGE_SHIFT; ++ struct blk_nvmem *bnv = priv; ++ size_t bytes_left = bytes; ++ struct file *bdev_file; ++ struct folio *folio; ++ void *p; ++ int ret = 0; ++ ++ bdev_file = bdev_file_open_by_dev(bnv->dev->devt, mode, priv, NULL); ++ if (!bdev_file) ++ return -ENODEV; ++ ++ if (IS_ERR(bdev_file)) ++ return PTR_ERR(bdev_file); ++ ++ while (bytes_left) { ++ folio = read_mapping_folio(bdev_file->f_mapping, f_index++, NULL); ++ if (IS_ERR(folio)) { ++ ret = PTR_ERR(folio); ++ goto err_release_bdev; ++ } ++ to_read = min_t(unsigned long, bytes_left, PAGE_SIZE - offs); ++ p = folio_address(folio) + offset_in_folio(folio, offs); ++ memcpy(val, p, to_read); ++ offs = 0; ++ bytes_left -= to_read; ++ val += to_read; ++ folio_put(folio); ++ } ++ ++err_release_bdev: ++ fput(bdev_file); ++ ++ return ret; ++} ++ ++static int blk_nvmem_register(struct device *dev) ++{ ++ struct block_device *bdev = dev_to_bdev(dev); ++ struct device_node *np = dev_of_node(dev); ++ struct nvmem_config config = {}; ++ struct blk_nvmem *bnv; ++ ++ /* skip devices which do not have a device tree node */ ++ if (!np) ++ return 0; ++ ++ /* skip devices without an nvmem layout defined */ ++ if (!of_get_child_by_name(np, "nvmem-layout")) ++ return 0; ++ ++ /* ++ * skip devices which don't have GENHD_FL_NVMEM set ++ * ++ * This flag is used for mtdblock and ubiblock devices because ++ * both, MTD and UBI already implement their own NVMEM provider. ++ * To avoid registering multiple NVMEM providers for the same ++ * device node, don't register the block NVMEM provider for them. ++ */ ++ if (!(bdev->bd_disk->flags & GENHD_FL_NVMEM)) ++ return 0; ++ ++ /* ++ * skip block device too large to be represented as NVMEM devices ++ * which are using an 'int' as address ++ */ ++ if (bdev_nr_bytes(bdev) > INT_MAX) ++ return -EFBIG; ++ ++ bnv = kzalloc(sizeof(struct blk_nvmem), GFP_KERNEL); ++ if (!bnv) ++ return -ENOMEM; ++ ++ config.id = NVMEM_DEVID_NONE; ++ config.dev = &bdev->bd_device; ++ config.name = dev_name(&bdev->bd_device); ++ config.owner = THIS_MODULE; ++ config.priv = bnv; ++ config.reg_read = blk_nvmem_reg_read; ++ config.size = bdev_nr_bytes(bdev); ++ config.word_size = 1; ++ config.stride = 1; ++ config.read_only = true; ++ config.root_only = true; ++ config.ignore_wp = true; ++ config.of_node = to_of_node(dev->fwnode); ++ ++ bnv->dev = &bdev->bd_device; ++ bnv->nvmem = nvmem_register(&config); ++ if (IS_ERR(bnv->nvmem)) { ++ dev_err_probe(&bdev->bd_device, PTR_ERR(bnv->nvmem), ++ "Failed to register NVMEM device\n"); ++ ++ kfree(bnv); ++ return PTR_ERR(bnv->nvmem); ++ } ++ ++ mutex_lock(&devices_mutex); ++ list_add_tail(&bnv->list, &nvmem_devices); ++ mutex_unlock(&devices_mutex); ++ ++ return 0; ++} ++ ++static void blk_nvmem_unregister(struct device *dev) ++{ ++ struct blk_nvmem *bnv_c, *bnv = NULL; ++ ++ mutex_lock(&devices_mutex); ++ list_for_each_entry(bnv_c, &nvmem_devices, list) { ++ if (bnv_c->dev == dev) { ++ bnv = bnv_c; ++ break; ++ } ++ } ++ ++ if (!bnv) { ++ mutex_unlock(&devices_mutex); ++ return; ++ } ++ ++ list_del(&bnv->list); ++ mutex_unlock(&devices_mutex); ++ nvmem_unregister(bnv->nvmem); ++ kfree(bnv); ++} ++ ++static int blk_nvmem_handler(struct notifier_block *this, unsigned long code, void *obj) ++{ ++ struct device *dev = (struct device *)obj; ++ ++ switch (code) { ++ case BLK_DEVICE_ADD: ++ return blk_nvmem_register(dev); ++ break; ++ case BLK_DEVICE_REMOVE: ++ blk_nvmem_unregister(dev); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block blk_nvmem_notifier = { ++ .notifier_call = blk_nvmem_handler, ++}; ++ ++static int __init blk_nvmem_init(void) ++{ ++ blk_register_notify(&blk_nvmem_notifier); ++ ++ return 0; ++} ++ ++static void __exit blk_nvmem_exit(void) ++{ ++ blk_unregister_notify(&blk_nvmem_notifier); ++} ++ ++module_init(blk_nvmem_init); ++module_exit(blk_nvmem_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_DESCRIPTION("block device NVMEM provider"); diff --git a/target/linux/generic/pending-6.12/457-mmc-block-set-GENHD_FL_NVMEM.patch b/target/linux/generic/pending-6.12/457-mmc-block-set-GENHD_FL_NVMEM.patch new file mode 100644 index 00000000000..955fa6676e9 --- /dev/null +++ b/target/linux/generic/pending-6.12/457-mmc-block-set-GENHD_FL_NVMEM.patch @@ -0,0 +1,22 @@ +From caac69565748349a3e532130a891e18dd5c36c5e Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 20 Jul 2023 17:36:44 +0100 +Subject: [PATCH 8/8] mmc: block: set GENHD_FL_NVMEM + +Set flag to consider MMC block devices as NVMEM providers. + +Signed-off-by: Daniel Golle +--- + drivers/mmc/core/block.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mmc/core/block.c ++++ b/drivers/mmc/core/block.c +@@ -2644,6 +2644,7 @@ static struct mmc_blk_data *mmc_blk_allo + md->disk->major = MMC_BLOCK_MAJOR; + md->disk->minors = perdev_minors; + md->disk->first_minor = devidx * perdev_minors; ++ md->disk->flags = GENHD_FL_NVMEM; + md->disk->fops = &mmc_bdops; + md->disk->private_data = md; + md->parent = parent; diff --git a/target/linux/generic/pending-6.12/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch b/target/linux/generic/pending-6.12/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch new file mode 100644 index 00000000000..2435133fa0b --- /dev/null +++ b/target/linux/generic/pending-6.12/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch @@ -0,0 +1,25 @@ +From: Felix Fietkau +Subject: kernel: disable cfi cmdset 0002 erase suspend + +on some platforms, erase suspend leads to data corruption and lockups when write +ops collide with erase ops. this has been observed on the buffalo wzr-hp-g300nh. +rather than play whack-a-mole with a hard to reproduce issue on a variety of devices, +simply disable erase suspend, as it will usually not produce any useful gain on +the small filesystems used on embedded hardware. + +Signed-off-by: Felix Fietkau +--- + drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -906,7 +906,7 @@ static int get_chip(struct map_info *map + return 0; + + case FL_ERASING: +- if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) || ++ if (1 /* no suspend */ || !cfip || !(cfip->EraseSuspend & (0x1|0x2)) || + !(mode == FL_READY || mode == FL_POINT || + (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)))) + goto sleep; diff --git a/target/linux/generic/pending-6.12/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch b/target/linux/generic/pending-6.12/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch new file mode 100644 index 00000000000..059d9673dcb --- /dev/null +++ b/target/linux/generic/pending-6.12/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch @@ -0,0 +1,17 @@ +From: George Kashperko +Subject: Issue map read after Write Buffer Load command to ensure chip is ready to receive data. + +Signed-off-by: George Kashperko +--- + drivers/mtd/chips/cfi_cmdset_0002.c | 1 + + 1 file changed, 1 insertion(+) +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -2050,6 +2050,7 @@ static int __xipram do_write_buffer(stru + + /* Write Buffer Load */ + map_write(map, CMD(0x25), cmd_adr); ++ (void) map_read(map, cmd_adr); + + chip->state = FL_WRITING_TO_BUFFER; + diff --git a/target/linux/generic/pending-6.12/476-mtd-spi-nor-add-eon-en25q128.patch b/target/linux/generic/pending-6.12/476-mtd-spi-nor-add-eon-en25q128.patch new file mode 100644 index 00000000000..afc5e48981e --- /dev/null +++ b/target/linux/generic/pending-6.12/476-mtd-spi-nor-add-eon-en25q128.patch @@ -0,0 +1,22 @@ +From: Piotr Dymacz +Subject: kernel/mtd: add support for EON EN25Q128 + +Signed-off-by: Piotr Dymacz +--- + drivers/mtd/spi-nor/spi-nor.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/eon.c ++++ b/drivers/mtd/spi-nor/eon.c +@@ -18,6 +18,11 @@ static const struct flash_info eon_nor_p + .name = "en25p64", + .size = SZ_8M, + }, { ++ .id = SNOR_ID(0x1c, 0x30, 0x18), ++ .name = "en25q128", ++ .size = SZ_16M, ++ .no_sfdp_flags = SECT_4K, ++ }, { + .id = SNOR_ID(0x1c, 0x30, 0x14), + .name = "en25q80a", + .size = SZ_1M, diff --git a/target/linux/generic/pending-6.12/477-mtd-spi-nor-add-eon-en25qx128a.patch b/target/linux/generic/pending-6.12/477-mtd-spi-nor-add-eon-en25qx128a.patch new file mode 100644 index 00000000000..4dd229bde9d --- /dev/null +++ b/target/linux/generic/pending-6.12/477-mtd-spi-nor-add-eon-en25qx128a.patch @@ -0,0 +1,24 @@ +From: Christian Marangi +Subject: kernel/mtd: add support for EON EN25QX128A + +Add support for EON EN25QX128A with no flags as it does +support SFDP parsing. + +Signed-off-by: Christian Marangi +--- + drivers/mtd/spi-nor/spi-nor.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/eon.c ++++ b/drivers/mtd/spi-nor/eon.c +@@ -23,6 +23,10 @@ static const struct flash_info eon_nor_p + .size = SZ_16M, + .no_sfdp_flags = SECT_4K, + }, { ++ .id = SNOR_ID(0x1c, 0x71, 0x18), ++ .name = "en25qx128a", ++ .size = SZ_16M, ++ }, { + .id = SNOR_ID(0x1c, 0x30, 0x14), + .name = "en25q80a", + .size = SZ_1M, diff --git a/target/linux/generic/pending-6.12/479-mtd-spi-nor-add-xtx-xt25f128b.patch b/target/linux/generic/pending-6.12/479-mtd-spi-nor-add-xtx-xt25f128b.patch new file mode 100644 index 00000000000..be8bafa76af --- /dev/null +++ b/target/linux/generic/pending-6.12/479-mtd-spi-nor-add-xtx-xt25f128b.patch @@ -0,0 +1,84 @@ +From patchwork Thu Feb 6 17:19:41 2020 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 1234465 +Date: Thu, 6 Feb 2020 19:19:41 +0200 +From: Daniel Golle +To: linux-mtd@lists.infradead.org +Subject: [PATCH v2] mtd: spi-nor: Add support for xt25f128b chip +Message-ID: <20200206171941.GA2398@makrotopia.org> +MIME-Version: 1.0 +Content-Disposition: inline +List-Subscribe: , + +Cc: Eitan Cohen , Piotr Dymacz , + Tudor Ambarus +Sender: "linux-mtd" +Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org + +Add XT25F128B made by XTX Technology (Shenzhen) Limited. +This chip supports dual and quad read and uniform 4K-byte erase. +Verified on Teltonika RUT955 which comes with XT25F128B in recent +versions of the device. + +Signed-off-by: Daniel Golle +Signed-off-by: Felix Fietkau +--- + drivers/mtd/spi-nor/spi-nor.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mtd/spi-nor/Makefile ++++ b/drivers/mtd/spi-nor/Makefile +@@ -14,6 +14,7 @@ spi-nor-objs += spansion.o + spi-nor-objs += sst.o + spi-nor-objs += winbond.o + spi-nor-objs += xmc.o ++spi-nor-objs += xtx.o + spi-nor-$(CONFIG_DEBUG_FS) += debugfs.o + obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o + +--- /dev/null ++++ b/drivers/mtd/spi-nor/xtx.c +@@ -0,0 +1,20 @@ ++// SPDX-License-Identifier: GPL-2.0 ++#include ++ ++#include "core.h" ++ ++static const struct flash_info xtx_parts[] = { ++ /* XTX Technology (Shenzhen) Limited */ ++ { ++ .id = SNOR_ID(0x0B, 0x40, 0x18), ++ .name = "xt25f128b", ++ .size = SZ_16M, ++ .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, ++ }, ++}; ++ ++const struct spi_nor_manufacturer spi_nor_xtx = { ++ .name = "xtx", ++ .parts = xtx_parts, ++ .nparts = ARRAY_SIZE(xtx_parts), ++}; +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -1979,6 +1979,7 @@ static const struct spi_nor_manufacturer + &spi_nor_sst, + &spi_nor_winbond, + &spi_nor_xmc, ++ &spi_nor_xtx, + }; + + static const struct flash_info spi_nor_generic_flash = { +--- a/drivers/mtd/spi-nor/core.h ++++ b/drivers/mtd/spi-nor/core.h +@@ -593,6 +593,7 @@ extern const struct spi_nor_manufacturer + extern const struct spi_nor_manufacturer spi_nor_sst; + extern const struct spi_nor_manufacturer spi_nor_winbond; + extern const struct spi_nor_manufacturer spi_nor_xmc; ++extern const struct spi_nor_manufacturer spi_nor_xtx; + + extern const struct attribute_group *spi_nor_sysfs_groups[]; + diff --git a/target/linux/generic/pending-6.12/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch b/target/linux/generic/pending-6.12/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch new file mode 100644 index 00000000000..c4caf732ab5 --- /dev/null +++ b/target/linux/generic/pending-6.12/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch @@ -0,0 +1,25 @@ +From d68b4aa22e8c625685bfad642dd7337948dc0ad1 Mon Sep 17 00:00:00 2001 +From: Koen Vandeputte +Date: Mon, 6 Jan 2020 13:07:56 +0100 +Subject: [PATCH] mtd: spi-nor: add support for Gigadevice GD25D05 + +Signed-off-by: Koen Vandeputte +--- + drivers/mtd/spi-nor/spi-nor.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/mtd/spi-nor/gigadevice.c ++++ b/drivers/mtd/spi-nor/gigadevice.c +@@ -35,6 +35,12 @@ static const struct spi_nor_fixups gd25q + + static const struct flash_info gigadevice_nor_parts[] = { + { ++ .id = SNOR_ID(0xc8, 0x40, 0x10), ++ .name = "gd25q05", ++ .size = SZ_64K, ++ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, ++ .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, ++ }, { + .id = SNOR_ID(0xc8, 0x40, 0x15), + .name = "gd25q16", + .size = SZ_2M, diff --git a/target/linux/generic/pending-6.12/482-mtd-spi-nor-add-gd25q512.patch b/target/linux/generic/pending-6.12/482-mtd-spi-nor-add-gd25q512.patch new file mode 100644 index 00000000000..2b34d145ee3 --- /dev/null +++ b/target/linux/generic/pending-6.12/482-mtd-spi-nor-add-gd25q512.patch @@ -0,0 +1,25 @@ +From f8943df3beb0d3f9754bb35320c3a378727175a8 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Thu, 14 Jul 2022 08:38:07 +0200 +Subject: [PATCH] spi-nor/gigadevic: add gd25q512 + +--- + drivers/mtd/spi-nor/gigadevice.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/mtd/spi-nor/gigadevice.c ++++ b/drivers/mtd/spi-nor/gigadevice.c +@@ -71,6 +71,13 @@ static const struct flash_info gigadevic + .fixups = &gd25q256_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES, + }, { ++ .id = SNOR_ID(0xc8, 0x40, 0x20), ++ .name = "gd25q512", ++ .size = SZ_64M, ++ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, ++ .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, ++ .fixup_flags = SPI_NOR_4B_OPCODES, ++ }, { + .id = SNOR_ID(0xc8, 0x60, 0x16), + .name = "gd25lq32", + .size = SZ_4M, diff --git a/target/linux/generic/pending-6.12/484-mtd-spi-nor-add-esmt-f25l16pa.patch b/target/linux/generic/pending-6.12/484-mtd-spi-nor-add-esmt-f25l16pa.patch new file mode 100644 index 00000000000..1e296eedceb --- /dev/null +++ b/target/linux/generic/pending-6.12/484-mtd-spi-nor-add-esmt-f25l16pa.patch @@ -0,0 +1,27 @@ +From 87363cc0e522de3294ea6ae10fb468d2a8d6fb2f Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 12:17:21 +0200 +Subject: [PATCH] spi-nor/esmt.c: add esmt f25l16pa + +This fixes support for Dongwon T&I DW02-412H which uses F25L16PA(2S) +flash. + +--- + drivers/mtd/spi-nor/esmt.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/mtd/spi-nor/esmt.c ++++ b/drivers/mtd/spi-nor/esmt.c +@@ -10,6 +10,12 @@ + + static const struct flash_info esmt_nor_parts[] = { + { ++ .id = SNOR_ID(0x8c, 0x21, 0x15), ++ .name = "f25l16pa-2s", ++ .size = SZ_2M, ++ .flags = SPI_NOR_HAS_LOCK, ++ .no_sfdp_flags = SECT_4K, ++ }, { + .id = SNOR_ID(0x8c, 0x20, 0x16), + .name = "f25l32pa", + .size = SZ_4M, diff --git a/target/linux/generic/pending-6.12/485-mtd-spi-nor-add-xmc-xm25qh128c.patch b/target/linux/generic/pending-6.12/485-mtd-spi-nor-add-xmc-xm25qh128c.patch new file mode 100644 index 00000000000..886d7558166 --- /dev/null +++ b/target/linux/generic/pending-6.12/485-mtd-spi-nor-add-xmc-xm25qh128c.patch @@ -0,0 +1,27 @@ +From f6b33d850f7f12555df2fa0e3349b33427bf5890 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 12:19:01 +0200 +Subject: [PATCH] spi-nor/xmc.c: add xm25qh128c + +The XMC XM25QH128C is a 16MB SPI NOR chip. The patch is verified on +Ruijie RG-EW3200GX PRO. +Datasheet available at https://www.xmcwh.com/uploads/435/XM25QH128C.pdf + +--- + drivers/mtd/spi-nor/xmc.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/mtd/spi-nor/xmc.c ++++ b/drivers/mtd/spi-nor/xmc.c +@@ -19,6 +19,11 @@ static const struct flash_info xmc_nor_p + .name = "XM25QH128A", + .size = SZ_16M, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, ++ }, { ++ .id = SNOR_ID(0x20, 0x40, 0x18), ++ .name = "XM25QH128C", ++ .size = SZ_16M, ++ .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + }, + }; + diff --git a/target/linux/generic/pending-6.12/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch b/target/linux/generic/pending-6.12/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch new file mode 100644 index 00000000000..65d83840237 --- /dev/null +++ b/target/linux/generic/pending-6.12/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch @@ -0,0 +1,170 @@ +From f32085fc0b87049491b07e198d924d738a1a2834 Mon Sep 17 00:00:00 2001 +From: Daniel Danzberger +Date: Wed, 3 Aug 2022 17:31:03 +0200 +Subject: [PATCH] mtd: spinand: Add support for Etron EM73D044VCx + +Airoha is a new ARM platform based on Cortex-A53 which has recently been +merged into linux-next. + +Due to BootROM limitations on this platform, the Cortex-A53 can't run in +Aarch64 mode and code must be compiled for 32-Bit ARM. + +This support is based mostly on those linux-next commits backported +for kernel 5.15. + +Patches: +1 - platform support = linux-next +2 - clock driver = linux-next +3 - gpio driver = linux-next +4 - linux,usable-memory-range dts support = linux-next +5 - mtd spinand driver +6 - spi driver +7 - pci driver (kconfig only, uses mediatek PCI) = linux-next + +Still missing: +- Ethernet driver +- Sysupgrade support + +A.t.m there exists one subtarget EN7523 with only one evaluation +board. + +The initramfs can be run with the following commands from u-boot: +- +u-boot> setenv bootfile \ + openwrt-airoha-airoha_en7523-evb-initramfs-kernel.bin +u-boot> tftpboot +u-boot> bootm 0x81800000 +- + +Submitted-by: Daniel Danzberger + +--- a/drivers/mtd/nand/spi/Makefile ++++ b/drivers/mtd/nand/spi/Makefile +@@ -1,4 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0 +-spinand-objs := core.o alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o macronix.o +-spinand-objs += micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o ++spinand-objs := core.o alliancememory.o ato.o esmt.o etron.o fmsh.o foresee.o gigadevice.o ++spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o + obj-$(CONFIG_MTD_SPI_NAND) += spinand.o +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -1186,6 +1186,7 @@ static const struct spinand_manufacturer + &ato_spinand_manufacturer, + &esmt_8c_spinand_manufacturer, + &esmt_c8_spinand_manufacturer, ++ &etron_spinand_manufacturer, + &fmsh_spinand_manufacturer, + &foresee_spinand_manufacturer, + &gigadevice_spinand_manufacturer, +--- /dev/null ++++ b/drivers/mtd/nand/spi/etron.c +@@ -0,0 +1,98 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++#include ++ ++#define SPINAND_MFR_ETRON 0xd5 ++ ++ ++static SPINAND_OP_VARIANTS(read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(write_cache_variants, ++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_variants, ++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ ++static int etron_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 72; ++ oobregion->length = 56; ++ ++ return 0; ++} ++ ++static int etron_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 1; ++ oobregion->length = 71; ++ ++ return 0; ++} ++ ++static int etron_ecc_get_status(struct spinand_device *spinand, u8 status) ++{ ++ switch (status & STATUS_ECC_MASK) { ++ case STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case STATUS_ECC_HAS_BITFLIPS: ++ /* Between 1-7 bitflips were corrected */ ++ return 7; ++ ++ case STATUS_ECC_MASK: ++ /* Maximum bitflips were corrected */ ++ return 8; ++ ++ case STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct mtd_ooblayout_ops etron_ooblayout = { ++ .ecc = etron_ooblayout_ecc, ++ .free = etron_ooblayout_free, ++}; ++ ++static const struct spinand_info etron_spinand_table[] = { ++ SPINAND_INFO("EM73D044VCx", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x1f), ++ // bpc, pagesize, oobsize, pagesperblock, bperlun, maxbadplun, ppl, lpt, #t ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)), ++}; ++ ++static const struct spinand_manufacturer_ops etron_spinand_manuf_ops = { ++}; ++ ++const struct spinand_manufacturer etron_spinand_manufacturer = { ++ .id = SPINAND_MFR_ETRON, ++ .name = "Etron", ++ .chips = etron_spinand_table, ++ .nchips = ARRAY_SIZE(etron_spinand_table), ++ .ops = &etron_spinand_manuf_ops, ++}; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -264,6 +264,7 @@ extern const struct spinand_manufacturer + extern const struct spinand_manufacturer ato_spinand_manufacturer; + extern const struct spinand_manufacturer esmt_8c_spinand_manufacturer; + extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; ++extern const struct spinand_manufacturer etron_spinand_manufacturer; + extern const struct spinand_manufacturer fmsh_spinand_manufacturer; + extern const struct spinand_manufacturer foresee_spinand_manufacturer; + extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; diff --git a/target/linux/generic/pending-6.12/488-mtd-spi-nor-add-xmc-xm25qh64c.patch b/target/linux/generic/pending-6.12/488-mtd-spi-nor-add-xmc-xm25qh64c.patch new file mode 100644 index 00000000000..6e65c55b21e --- /dev/null +++ b/target/linux/generic/pending-6.12/488-mtd-spi-nor-add-xmc-xm25qh64c.patch @@ -0,0 +1,25 @@ +From: Joe Mullally +Subject: mtd/spi-nor/xmc: add support for XMC XM25QH64C + +The XMC XM25QH64C is a 8MB SPI NOR chip. The patch is verified on TL-WPA8631P v3. +Datasheet available at https://www.xmcwh.com/uploads/442/XM25QH64C.pdf + +Signed-off-by: Joe Mullally +--- + drivers/mtd/spi-nor/xmc.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/mtd/spi-nor/xmc.c ++++ b/drivers/mtd/spi-nor/xmc.c +@@ -15,6 +15,11 @@ static const struct flash_info xmc_nor_p + .size = SZ_8M, + .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + }, { ++ .id = SNOR_ID(0x20, 0x40, 0x17), ++ .name = "XM25QH64C", ++ .size = SZ_8M, ++ .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, ++ }, { + .id = SNOR_ID(0x20, 0x70, 0x18), + .name = "XM25QH128A", + .size = SZ_16M, diff --git a/target/linux/generic/pending-6.12/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch b/target/linux/generic/pending-6.12/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch new file mode 100644 index 00000000000..27b86111004 --- /dev/null +++ b/target/linux/generic/pending-6.12/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch @@ -0,0 +1,104 @@ +From: Daniel Golle +Subject: ubi: auto-attach mtd device named "ubi" or "data" on boot + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/build.c | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +--- a/drivers/mtd/ubi/build.c ++++ b/drivers/mtd/ubi/build.c +@@ -1263,6 +1263,80 @@ static struct mtd_notifier ubi_mtd_notif + .remove = ubi_notify_remove, + }; + ++ ++/* ++ * This function tries attaching mtd partitions named either "ubi" or "data" ++ * during boot. ++ */ ++static void __init ubi_auto_attach(void) ++{ ++ int err; ++ struct mtd_info *mtd; ++ struct device_node *np; ++ loff_t offset = 0; ++ size_t len; ++ char magic[4]; ++ ++ /* try attaching mtd device named "ubi" or "data" */ ++ mtd = open_mtd_device("ubi"); ++ if (IS_ERR(mtd)) ++ mtd = open_mtd_device("data"); ++ ++ if (IS_ERR(mtd)) ++ return; ++ ++ /* skip "linux,ubi" mtd as it has already been attached */ ++ np = mtd_get_of_node(mtd); ++ if (of_device_is_compatible(np, "linux,ubi")) ++ goto cleanup; ++ ++ /* get the first not bad block */ ++ if (mtd_can_have_bb(mtd)) ++ while (mtd_block_isbad(mtd, offset)) { ++ offset += mtd->erasesize; ++ ++ if (offset > mtd->size) { ++ pr_err("UBI error: Failed to find a non-bad " ++ "block on mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ } ++ ++ /* check if the read from flash was successful */ ++ err = mtd_read(mtd, offset, 4, &len, (void *) magic); ++ if ((err && !mtd_is_bitflip(err)) || len != 4) { ++ pr_err("UBI error: unable to read from mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ ++ /* check for a valid ubi magic */ ++ if (strncmp(magic, "UBI#", 4)) { ++ pr_err("UBI error: no valid UBI magic found inside mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ ++ /* don't auto-add media types where UBI doesn't makes sense */ ++ if (mtd->type != MTD_NANDFLASH && ++ mtd->type != MTD_NORFLASH && ++ mtd->type != MTD_DATAFLASH && ++ mtd->type != MTD_MLCNANDFLASH) ++ goto cleanup; ++ ++ mutex_lock(&ubi_devices_mutex); ++ pr_notice("UBI: auto-attach mtd%d\n", mtd->index); ++ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0, false, false); ++ mutex_unlock(&ubi_devices_mutex); ++ if (err < 0) { ++ pr_err("UBI error: cannot attach mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ ++ return; ++ ++cleanup: ++ put_mtd_device(mtd); ++} ++ + static int __init ubi_init_attach(void) + { + int err, i, k; +@@ -1314,6 +1388,12 @@ static int __init ubi_init_attach(void) + } + } + ++ /* auto-attach mtd devices only if built-in to the kernel and no ubi.mtd ++ * parameter was given */ ++ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && ++ !ubi_is_module() && !mtd_devs) ++ ubi_auto_attach(); ++ + return 0; + + out_detach: diff --git a/target/linux/generic/pending-6.12/491-ubi-auto-create-ubiblock-device-for-rootfs.patch b/target/linux/generic/pending-6.12/491-ubi-auto-create-ubiblock-device-for-rootfs.patch new file mode 100644 index 00000000000..44c3bf36fbb --- /dev/null +++ b/target/linux/generic/pending-6.12/491-ubi-auto-create-ubiblock-device-for-rootfs.patch @@ -0,0 +1,77 @@ +From: Daniel Golle +Subject: ubi: auto-create ubiblock device for rootfs + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/block.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 42 insertions(+) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -575,10 +575,47 @@ match_volume_desc(struct ubi_volume_info + return true; + } + ++#define UBIFS_NODE_MAGIC 0x06101831 ++static inline int ubi_vol_is_ubifs(struct ubi_volume_desc *desc) ++{ ++ int ret; ++ uint32_t magic_of, magic; ++ ret = ubi_read(desc, 0, (char *)&magic_of, 0, 4); ++ if (ret) ++ return 0; ++ magic = le32_to_cpu(magic_of); ++ return magic == UBIFS_NODE_MAGIC; ++} ++ ++static void ubiblock_create_auto_rootfs(struct ubi_volume_info *vi) ++{ ++ int ret, is_ubifs; ++ struct ubi_volume_desc *desc; ++ ++ if (strcmp(vi->name, "rootfs") && ++ strcmp(vi->name, "fit")) ++ return; ++ ++ desc = ubi_open_volume(vi->ubi_num, vi->vol_id, UBI_READONLY); ++ if (IS_ERR(desc)) ++ return; ++ ++ is_ubifs = ubi_vol_is_ubifs(desc); ++ ubi_close_volume(desc); ++ if (is_ubifs) ++ return; ++ ++ ret = ubiblock_create(vi); ++ if (ret) ++ pr_err("UBI error: block: can't add '%s' volume, err=%d\n", ++ vi->name, ret); ++} ++ + static void + ubiblock_create_from_param(struct ubi_volume_info *vi) + { + int i, ret = 0; ++ bool got_param = false; + struct ubiblock_param *p; + + /* +@@ -591,6 +628,7 @@ ubiblock_create_from_param(struct ubi_vo + if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id)) + continue; + ++ got_param = true; + ret = ubiblock_create(vi); + if (ret) { + pr_err( +@@ -599,6 +637,10 @@ ubiblock_create_from_param(struct ubi_vo + } + break; + } ++ ++ /* auto-attach "rootfs" volume if existing and non-ubifs */ ++ if (!got_param && IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV)) ++ ubiblock_create_auto_rootfs(vi); + } + + static int ubiblock_notify(struct notifier_block *nb, diff --git a/target/linux/generic/pending-6.12/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch b/target/linux/generic/pending-6.12/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch new file mode 100644 index 00000000000..7a4c6334382 --- /dev/null +++ b/target/linux/generic/pending-6.12/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch @@ -0,0 +1,54 @@ +From: Daniel Golle +Subject: try auto-mounting ubi0:rootfs in init/do_mounts.c + +Signed-off-by: Daniel Golle +--- + init/do_mounts.c | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -250,7 +250,30 @@ retry: + out: + put_page(page); + } +- ++ ++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV ++static int __init mount_ubi_rootfs(void) ++{ ++ int flags = MS_SILENT; ++ int err, tried = 0; ++ ++ while (tried < 2) { ++ err = do_mount_root("ubi0:rootfs", "ubifs", flags, \ ++ root_mount_data); ++ switch (err) { ++ case -EACCES: ++ flags |= MS_RDONLY; ++ tried++; ++ break; ++ default: ++ return err; ++ } ++ } ++ ++ return -EINVAL; ++} ++#endif ++ + #ifdef CONFIG_ROOT_NFS + + #define NFSROOT_TIMEOUT_MIN 5 +@@ -387,6 +410,11 @@ static inline void mount_block_root(char + + void __init mount_root(char *root_device_name) + { ++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV ++ if (!mount_ubi_rootfs()) ++ return; ++#endif ++ + switch (ROOT_DEV) { + case Root_NFS: + mount_nfs_root(); diff --git a/target/linux/generic/pending-6.12/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch b/target/linux/generic/pending-6.12/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch new file mode 100644 index 00000000000..4007c8a8bd1 --- /dev/null +++ b/target/linux/generic/pending-6.12/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch @@ -0,0 +1,34 @@ +From: Daniel Golle +Subject: ubi: set ROOT_DEV to ubiblock "rootfs" if unset + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/block.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -41,6 +41,7 @@ + #include + #include + #include ++#include + + #include "ubi-media.h" + #include "ubi.h" +@@ -431,6 +432,15 @@ int ubiblock_create(struct ubi_volume_in + dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)", + dev->ubi_num, dev->vol_id, vi->name); + mutex_unlock(&devices_mutex); ++ ++ if (!strcmp(vi->name, "rootfs") && ++ IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && ++ ROOT_DEV == 0) { ++ pr_notice("ubiblock: device ubiblock%d_%d (%s) set to be root filesystem\n", ++ dev->ubi_num, dev->vol_id, vi->name); ++ ROOT_DEV = MKDEV(gd->major, gd->first_minor); ++ } ++ + return 0; + + out_remove_minor: diff --git a/target/linux/generic/pending-6.12/494-mtd-ubi-add-EOF-marker-support.patch b/target/linux/generic/pending-6.12/494-mtd-ubi-add-EOF-marker-support.patch new file mode 100644 index 00000000000..413431755f1 --- /dev/null +++ b/target/linux/generic/pending-6.12/494-mtd-ubi-add-EOF-marker-support.patch @@ -0,0 +1,60 @@ +From: Gabor Juhos +Subject: mtd: add EOF marker support to the UBI layer + +Signed-off-by: Gabor Juhos +--- + drivers/mtd/ubi/attach.c | 25 ++++++++++++++++++++++--- + drivers/mtd/ubi/ubi.h | 1 + + 2 files changed, 23 insertions(+), 3 deletions(-) + +--- a/drivers/mtd/ubi/attach.c ++++ b/drivers/mtd/ubi/attach.c +@@ -926,6 +926,13 @@ static bool vol_ignored(int vol_id) + #endif + } + ++static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech) ++{ ++ return ech->padding1[0] == 'E' && ++ ech->padding1[1] == 'O' && ++ ech->padding1[2] == 'F'; ++} ++ + /** + * scan_peb - scan and process UBI headers of a PEB. + * @ubi: UBI device description object +@@ -958,9 +965,21 @@ static int scan_peb(struct ubi_device *u + return 0; + } + +- err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); +- if (err < 0) +- return err; ++ if (!ai->eof_found) { ++ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); ++ if (err < 0) ++ return err; ++ ++ if (ec_hdr_has_eof(ech)) { ++ pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n", ++ pnum); ++ ai->eof_found = true; ++ } ++ } ++ ++ if (ai->eof_found) ++ err = UBI_IO_FF_BITFLIPS; ++ + switch (err) { + case 0: + break; +--- a/drivers/mtd/ubi/ubi.h ++++ b/drivers/mtd/ubi/ubi.h +@@ -778,6 +778,7 @@ struct ubi_attach_info { + int mean_ec; + uint64_t ec_sum; + int ec_count; ++ bool eof_found; + struct kmem_cache *aeb_slab_cache; + struct ubi_ec_hdr *ech; + struct ubi_vid_io_buf *vidb; diff --git a/target/linux/generic/pending-6.12/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch b/target/linux/generic/pending-6.12/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch new file mode 100644 index 00000000000..01f3b9ec2da --- /dev/null +++ b/target/linux/generic/pending-6.12/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch @@ -0,0 +1,52 @@ +From 5734c6669fba7ddb5ef491ccff7159d15dba0b59 Mon Sep 17 00:00:00 2001 +From: Bernhard Frauendienst +Date: Wed, 5 Sep 2018 01:32:51 +0200 +Subject: [PATCH 496/497] dt-bindings: add bindings for mtd-concat devices + +Document virtual mtd-concat device bindings. + +Signed-off-by: Bernhard Frauendienst +--- + .../devicetree/bindings/mtd/mtd-concat.txt | 36 +++++++++++++++++++ + 1 file changed, 36 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/mtd-concat.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/mtd-concat.txt +@@ -0,0 +1,36 @@ ++Virtual MTD concat device ++ ++Requires properties: ++- devices: list of phandles to mtd nodes that should be concatenated ++ ++Example: ++ ++&spi { ++ flash0: flash@0 { ++ ... ++ }; ++ flash1: flash@1 { ++ ... ++ }; ++}; ++ ++flash { ++ compatible = "mtd-concat"; ++ ++ devices = <&flash0 &flash1>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ ++ partition@0 { ++ label = "boot"; ++ reg = <0x0000000 0x0040000>; ++ read-only; ++ }; ++ ++ partition@40000 { ++ label = "firmware"; ++ reg = <0x0040000 0x1fc0000>; ++ }; ++ } ++} diff --git a/target/linux/generic/pending-6.12/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch b/target/linux/generic/pending-6.12/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch new file mode 100644 index 00000000000..cda5e106eb4 --- /dev/null +++ b/target/linux/generic/pending-6.12/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch @@ -0,0 +1,215 @@ +From e53f712d8eac71f54399b61038ccf87d2cee99d7 Mon Sep 17 00:00:00 2001 +From: Bernhard Frauendienst +Date: Sat, 25 Aug 2018 12:35:22 +0200 +Subject: [PATCH 497/497] mtd: mtdconcat: add dt driver for concat devices + +Some mtd drivers like physmap variants have support for concatenating +multiple mtd devices, but there is no generic way to define such a +concat device from within the device tree. + +This is useful for some SoC boards that use multiple flash chips as +memory banks of a single mtd device, with partitions spanning chip +borders. + +This commit adds a driver for creating virtual mtd-concat devices. They +must have a compatible = "mtd-concat" line, and define a list of devices +to concat in the 'devices' property, for example: + +flash { + compatible = "mtd-concat"; + + devices = <&flash0 &flash1>; + + partitions { + ... + }; +}; + +The driver is added to the very end of the mtd Makefile to increase the +likelyhood of all child devices already being loaded at the time of +probing, preventing unnecessary deferred probes. + +Signed-off-by: Bernhard Frauendienst +--- + drivers/mtd/Kconfig | 2 + + drivers/mtd/Makefile | 3 + + drivers/mtd/composite/Kconfig | 12 +++ + drivers/mtd/composite/Makefile | 6 ++ + drivers/mtd/composite/virt_concat.c | 128 ++++++++++++++++++++++++++++ + 5 files changed, 151 insertions(+) + create mode 100644 drivers/mtd/composite/Kconfig + create mode 100644 drivers/mtd/composite/Makefile + create mode 100644 drivers/mtd/composite/virt_concat.c + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -241,4 +241,6 @@ source "drivers/mtd/ubi/Kconfig" + + source "drivers/mtd/hyperbus/Kconfig" + ++source "drivers/mtd/composite/Kconfig" ++ + endif # MTD +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -33,3 +33,6 @@ obj-y += chips/ lpddr/ maps/ devices/ n + obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ + obj-$(CONFIG_MTD_UBI) += ubi/ + obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/ ++ ++# Composite drivers must be loaded last ++obj-y += composite/ +--- /dev/null ++++ b/drivers/mtd/composite/Kconfig +@@ -0,0 +1,12 @@ ++menu "Composite MTD device drivers" ++ depends on MTD!=n ++ ++config MTD_VIRT_CONCAT ++ tristate "Virtual concat MTD device" ++ help ++ This driver allows creation of a virtual MTD concat device, which ++ concatenates multiple underlying MTD devices to a single device. ++ This is required by some SoC boards where multiple memory banks are ++ used as one device with partitions spanning across device boundaries. ++ ++endmenu +--- /dev/null ++++ b/drivers/mtd/composite/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# linux/drivers/mtd/composite/Makefile ++# ++ ++obj-$(CONFIG_MTD_VIRT_CONCAT) += virt_concat.o +--- /dev/null ++++ b/drivers/mtd/composite/virt_concat.c +@@ -0,0 +1,127 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Virtual concat MTD device driver ++ * ++ * Copyright (C) 2018 Bernhard Frauendienst ++ * Author: Bernhard Frauendienst, kernel@nospam.obeliks.de ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * struct of_virt_concat - platform device driver data. ++ * @cmtd the final mtd_concat device ++ * @num_devices the number of devices in @devices ++ * @devices points to an array of devices already loaded ++ */ ++struct of_virt_concat { ++ struct mtd_info *cmtd; ++ int num_devices; ++ struct mtd_info **devices; ++}; ++ ++static void virt_concat_remove(struct platform_device *pdev) ++{ ++ struct of_virt_concat *info; ++ int i; ++ ++ info = platform_get_drvdata(pdev); ++ if (!info) ++ return; ++ ++ // unset data for when this is called after a probe error ++ platform_set_drvdata(pdev, NULL); ++ ++ if (info->cmtd) { ++ mtd_device_unregister(info->cmtd); ++ mtd_concat_destroy(info->cmtd); ++ } ++ ++ if (info->devices) { ++ for (i = 0; i < info->num_devices; i++) ++ put_mtd_device(info->devices[i]); ++ } ++} ++ ++static int virt_concat_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct of_phandle_iterator it; ++ struct of_virt_concat *info; ++ struct mtd_info *mtd; ++ int err = 0, count; ++ ++ count = of_count_phandle_with_args(node, "devices", NULL); ++ if (count <= 0) ++ return -EINVAL; ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ info->devices = devm_kcalloc(&pdev->dev, count, ++ sizeof(*(info->devices)), GFP_KERNEL); ++ if (!info->devices) { ++ err = -ENOMEM; ++ goto err_remove; ++ } ++ ++ platform_set_drvdata(pdev, info); ++ ++ of_for_each_phandle(&it, err, node, "devices", NULL, 0) { ++ mtd = of_get_mtd_device_by_node(it.node); ++ if (IS_ERR(mtd)) { ++ of_node_put(it.node); ++ err = -EPROBE_DEFER; ++ goto err_remove; ++ } ++ ++ info->devices[info->num_devices++] = mtd; ++ } ++ ++ info->cmtd = mtd_concat_create(info->devices, info->num_devices, ++ dev_name(&pdev->dev)); ++ if (!info->cmtd) { ++ err = -ENXIO; ++ goto err_remove; ++ } ++ ++ info->cmtd->dev.parent = &pdev->dev; ++ mtd_set_of_node(info->cmtd, node); ++ mtd_device_register(info->cmtd, NULL, 0); ++ ++ return 0; ++ ++err_remove: ++ virt_concat_remove(pdev); ++ ++ return err; ++} ++ ++static const struct of_device_id virt_concat_of_match[] = { ++ { .compatible = "mtd-concat", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, virt_concat_of_match); ++ ++static struct platform_driver virt_concat_driver = { ++ .probe = virt_concat_probe, ++ .remove = virt_concat_remove, ++ .driver = { ++ .name = "virt-mtdconcat", ++ .of_match_table = virt_concat_of_match, ++ }, ++}; ++ ++module_platform_driver(virt_concat_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Bernhard Frauendienst "); ++MODULE_DESCRIPTION("Virtual concat MTD device driver"); diff --git a/target/linux/generic/pending-6.12/500-fs_cdrom_dependencies.patch b/target/linux/generic/pending-6.12/500-fs_cdrom_dependencies.patch new file mode 100644 index 00000000000..7c143584a4f --- /dev/null +++ b/target/linux/generic/pending-6.12/500-fs_cdrom_dependencies.patch @@ -0,0 +1,52 @@ +From af7b91bcecce0eae24e90acd35d96ecee73e1407 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 12:21:15 +0200 +Subject: [PATCH] fs: add cdrom dependency + +--- + fs/hfs/Kconfig | 1 + + fs/hfsplus/Kconfig | 1 + + fs/isofs/Kconfig | 1 + + fs/udf/Kconfig | 1 + + 4 files changed, 4 insertions(+) + +--- a/fs/hfs/Kconfig ++++ b/fs/hfs/Kconfig +@@ -2,6 +2,7 @@ + config HFS_FS + tristate "Apple Macintosh file system support" + depends on BLOCK ++ select CDROM + select BUFFER_HEAD + select NLS + select LEGACY_DIRECT_IO +--- a/fs/hfsplus/Kconfig ++++ b/fs/hfsplus/Kconfig +@@ -2,6 +2,7 @@ + config HFSPLUS_FS + tristate "Apple Extended HFS file system support" + depends on BLOCK ++ select CDROM + select BUFFER_HEAD + select NLS + select NLS_UTF8 +--- a/fs/isofs/Kconfig ++++ b/fs/isofs/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + config ISO9660_FS + tristate "ISO 9660 CDROM file system support" ++ select CDROM + select BUFFER_HEAD + help + This is the standard file system used on CD-ROMs. It was previously +--- a/fs/udf/Kconfig ++++ b/fs/udf/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + config UDF_FS + tristate "UDF file system support" ++ select CDROM + select BUFFER_HEAD + select CRC_ITU_T + select NLS diff --git a/target/linux/generic/pending-6.12/510-block-add-uImage.FIT-subimage-block-driver.patch b/target/linux/generic/pending-6.12/510-block-add-uImage.FIT-subimage-block-driver.patch new file mode 100644 index 00000000000..74b079aa9b9 --- /dev/null +++ b/target/linux/generic/pending-6.12/510-block-add-uImage.FIT-subimage-block-driver.patch @@ -0,0 +1,757 @@ +From 3a200922ac4cbf4bfffdf86c0957b1401474e839 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 16 Nov 2022 12:49:52 +0000 +Subject: [PATCH 1/2] block: add uImage.FIT subimage block driver + +Add a small block driver which exposes filesystem sub-images contained +in U-Boot uImage.FIT images as block devices. + +The uImage.FIT image has to be stored directly on a block device or +partition, MTD device or partition, or UBI volume. + +The driver is intended for systems using the U-Boot bootloader and +uses the root device hint left by the bootloader (or the user) in +the 'chosen' section of the device-tree. + +Example: +/dts-v1/; +/ { + chosen { + rootdisk = <&mmc0_part3>; + }; +}; + +Signed-off-by: Daniel Golle +--- + MAINTAINERS | 6 + + drivers/block/Kconfig | 12 + + drivers/block/Makefile | 2 + + drivers/block/fitblk.c | 660 ++++++++++++++++++++++++++++++++++++ + drivers/block/open | 4 + + include/uapi/linux/fitblk.h | 10 + + 6 files changed, 694 insertions(+) + create mode 100644 drivers/block/fitblk.c + create mode 100644 drivers/block/open + create mode 100644 include/uapi/linux/fitblk.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -23677,6 +23677,12 @@ F: Documentation/filesystems/ubifs-authe + F: Documentation/filesystems/ubifs.rst + F: fs/ubifs/ + ++U-BOOT UIMAGE.FIT PARSER ++M: Daniel Golle ++L: linux-block@vger.kernel.org ++S: Maintained ++F: drivers/block/fitblk.c ++ + UBLK USERSPACE BLOCK DRIVER + M: Ming Lei + L: linux-block@vger.kernel.org +--- a/drivers/block/Kconfig ++++ b/drivers/block/Kconfig +@@ -363,6 +363,18 @@ config BLK_DEV_RUST_NULL + + If unsure, say N. + ++config UIMAGE_FIT_BLK ++ bool "uImage.FIT block driver" ++ help ++ This driver allows using filesystems contained in uImage.FIT images ++ by mapping them as block devices. ++ ++ It can currently not be built as a module due to libfdt symbols not ++ being exported. ++ ++ Say Y if you want to mount filesystems sub-images of a uImage.FIT ++ stored in a block device partition, mtdblock or ubiblock device. ++ + config BLK_DEV_RBD + tristate "Rados block device (RBD)" + depends on INET && BLOCK +--- a/drivers/block/Makefile ++++ b/drivers/block/Makefile +@@ -42,4 +42,6 @@ obj-$(CONFIG_BLK_DEV_NULL_BLK) += null_b + + obj-$(CONFIG_BLK_DEV_UBLK) += ublk_drv.o + ++obj-$(CONFIG_UIMAGE_FIT_BLK) += fitblk.o ++ + swim_mod-y := swim.o swim_asm.o +--- /dev/null ++++ b/drivers/block/fitblk.c +@@ -0,0 +1,660 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * uImage.FIT virtual block device driver. ++ * ++ * Copyright (C) 2023 Daniel Golle ++ * Copyright (C) 2007 Nick Piggin ++ * Copyright (C) 2007 Novell Inc. ++ * ++ * Initially derived from drivers/block/brd.c which is in parts derived from ++ * drivers/block/rd.c, and drivers/block/loop.c, copyright of their respective ++ * owners. ++ * ++ * uImage.FIT headers extracted from Das U-Boot ++ * (C) Copyright 2008 Semihalf ++ * (C) Copyright 2000-2005 ++ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define FIT_DEVICE_PREFIX "fit" ++ ++/* maximum number of pages used for the uImage.FIT index structure */ ++#define FIT_MAX_PAGES 1024 ++ ++/* minimum free sectors to map as read-write "remainder" volume */ ++#define MIN_FREE_SECT 16 ++ ++/* maximum number of mapped loadables */ ++#define MAX_FIT_LOADABLES 16 ++ ++/* constants for uImage.FIT structrure traversal */ ++#define FIT_IMAGES_PATH "/images" ++#define FIT_CONFS_PATH "/configurations" ++ ++/* hash/signature/key node */ ++#define FIT_HASH_NODENAME "hash" ++#define FIT_ALGO_PROP "algo" ++#define FIT_VALUE_PROP "value" ++#define FIT_IGNORE_PROP "uboot-ignore" ++#define FIT_SIG_NODENAME "signature" ++#define FIT_KEY_REQUIRED "required" ++#define FIT_KEY_HINT "key-name-hint" ++ ++/* cipher node */ ++#define FIT_CIPHER_NODENAME "cipher" ++#define FIT_ALGO_PROP "algo" ++ ++/* image node */ ++#define FIT_DATA_PROP "data" ++#define FIT_DATA_POSITION_PROP "data-position" ++#define FIT_DATA_OFFSET_PROP "data-offset" ++#define FIT_DATA_SIZE_PROP "data-size" ++#define FIT_TIMESTAMP_PROP "timestamp" ++#define FIT_DESC_PROP "description" ++#define FIT_ARCH_PROP "arch" ++#define FIT_TYPE_PROP "type" ++#define FIT_OS_PROP "os" ++#define FIT_COMP_PROP "compression" ++#define FIT_ENTRY_PROP "entry" ++#define FIT_LOAD_PROP "load" ++ ++/* configuration node */ ++#define FIT_KERNEL_PROP "kernel" ++#define FIT_FILESYSTEM_PROP "filesystem" ++#define FIT_RAMDISK_PROP "ramdisk" ++#define FIT_FDT_PROP "fdt" ++#define FIT_LOADABLE_PROP "loadables" ++#define FIT_DEFAULT_PROP "default" ++#define FIT_SETUP_PROP "setup" ++#define FIT_FPGA_PROP "fpga" ++#define FIT_FIRMWARE_PROP "firmware" ++#define FIT_STANDALONE_PROP "standalone" ++ ++/* fitblk driver data */ ++static const char *_fitblk_claim_ptr = "I belong to fitblk"; ++static const char *ubootver; ++struct device_node *rootdisk; ++static struct platform_device *pdev; ++static LIST_HEAD(fitblk_devices); ++static DEFINE_MUTEX(devices_mutex); ++refcount_t num_devs; ++ ++struct fitblk { ++ struct platform_device *pdev; ++ struct file *bdev_file; ++ sector_t start_sect; ++ struct gendisk *disk; ++ struct work_struct remove_work; ++ struct list_head list; ++ bool dead; ++}; ++ ++static int fitblk_open(struct gendisk *disk, fmode_t mode) ++{ ++ struct fitblk *fitblk = disk->private_data; ++ ++ if (fitblk->dead) ++ return -ENOENT; ++ ++ return 0; ++} ++ ++static void fitblk_release(struct gendisk *disk) ++{ ++ return; ++} ++ ++static void fitblk_submit_bio(struct bio *orig_bio) ++{ ++ struct bio *bio = orig_bio; ++ struct fitblk *fitblk = bio->bi_bdev->bd_disk->private_data; ++ ++ if (fitblk->dead) ++ return; ++ ++ /* mangle bio and re-submit */ ++ while (bio) { ++ bio->bi_iter.bi_sector += fitblk->start_sect; ++ bio->bi_bdev = file_bdev(fitblk->bdev_file); ++ bio = bio->bi_next; ++ } ++ submit_bio(orig_bio); ++} ++ ++static void fitblk_remove(struct fitblk *fitblk) ++{ ++ blk_mark_disk_dead(fitblk->disk); ++ mutex_lock(&devices_mutex); ++ fitblk->dead = true; ++ list_del(&fitblk->list); ++ mutex_unlock(&devices_mutex); ++ ++ schedule_work(&fitblk->remove_work); ++} ++ ++static int fitblk_ioctl(struct block_device *bdev, fmode_t mode, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct fitblk *fitblk = bdev->bd_disk->private_data; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ ++ if (fitblk->dead) ++ return -ENOENT; ++ ++ switch (cmd) { ++ case FITBLK_RELEASE: ++ fitblk_remove(fitblk); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct block_device_operations fitblk_fops = { ++ .owner = THIS_MODULE, ++ .ioctl = fitblk_ioctl, ++ .open = fitblk_open, ++ .release = fitblk_release, ++ .submit_bio = fitblk_submit_bio, ++}; ++ ++static void fitblk_purge(struct work_struct *work) ++{ ++ struct fitblk *fitblk = container_of(work, struct fitblk, remove_work); ++ ++ del_gendisk(fitblk->disk); ++ refcount_dec(&num_devs); ++ platform_device_del(fitblk->pdev); ++ platform_device_put(fitblk->pdev); ++ ++ if (refcount_dec_if_one(&num_devs)) { ++ sysfs_remove_link(&pdev->dev.kobj, "lower_dev"); ++ fput(fitblk->bdev_file); ++ } ++ ++ kfree(fitblk); ++} ++ ++static int add_fit_subimage_device(struct file *bdev_file, ++ unsigned int slot, sector_t start_sect, ++ sector_t nr_sect, bool readonly) ++{ ++ struct block_device *bdev = file_bdev(bdev_file); ++ struct fitblk *fitblk; ++ struct gendisk *disk; ++ int err; ++ ++ mutex_lock(&devices_mutex); ++ if (!refcount_inc_not_zero(&num_devs)) ++ return -EBADF; ++ ++ fitblk = kzalloc(sizeof(struct fitblk), GFP_KERNEL); ++ if (!fitblk) { ++ err = -ENOMEM; ++ goto out_unlock; ++ } ++ ++ fitblk->bdev_file = bdev_file; ++ fitblk->start_sect = start_sect; ++ INIT_WORK(&fitblk->remove_work, fitblk_purge); ++ ++ disk = blk_alloc_disk(&bdev->bd_disk->queue->limits, NUMA_NO_NODE); ++ if (!disk) { ++ err = -ENOMEM; ++ goto out_free_fitblk; ++ } ++ ++ disk->first_minor = 0; ++ disk->flags = bdev->bd_disk->flags | GENHD_FL_NO_PART; ++ disk->fops = &fitblk_fops; ++ disk->private_data = fitblk; ++ if (readonly) { ++ set_disk_ro(disk, 1); ++ snprintf(disk->disk_name, sizeof(disk->disk_name), FIT_DEVICE_PREFIX "%u", slot); ++ } else { ++ strcpy(disk->disk_name, FIT_DEVICE_PREFIX "rw"); ++ } ++ ++ set_capacity(disk, nr_sect); ++ disk->queue->queue_flags = bdev->bd_disk->queue->queue_flags; ++ ++ fitblk->disk = disk; ++ fitblk->pdev = platform_device_alloc(disk->disk_name, PLATFORM_DEVID_NONE); ++ if (!fitblk->pdev) { ++ err = -ENOMEM; ++ goto out_cleanup_disk; ++ } ++ ++ fitblk->pdev->dev.parent = &pdev->dev; ++ err = platform_device_add(fitblk->pdev); ++ if (err) ++ goto out_put_pdev; ++ ++ err = device_add_disk(&fitblk->pdev->dev, disk, NULL); ++ if (err) ++ goto out_del_pdev; ++ ++ if (!ROOT_DEV) ++ ROOT_DEV = disk->part0->bd_dev; ++ ++ list_add_tail(&fitblk->list, &fitblk_devices); ++ ++ mutex_unlock(&devices_mutex); ++ ++ return 0; ++ ++out_del_pdev: ++ platform_device_del(fitblk->pdev); ++out_put_pdev: ++ platform_device_put(fitblk->pdev); ++out_cleanup_disk: ++ put_disk(disk); ++out_free_fitblk: ++ kfree(fitblk); ++out_unlock: ++ refcount_dec(&num_devs); ++ mutex_unlock(&devices_mutex); ++ return err; ++} ++ ++static void fitblk_mark_dead(struct block_device *bdev, bool surprise) ++{ ++ struct list_head *n, *tmp; ++ struct fitblk *fitblk; ++ ++ mutex_lock(&devices_mutex); ++ list_for_each_safe(n, tmp, &fitblk_devices) { ++ fitblk = list_entry(n, struct fitblk, list); ++ if (file_bdev(fitblk->bdev_file) != bdev) ++ continue; ++ ++ fitblk->dead = true; ++ list_del(&fitblk->list); ++ /* removal needs to be deferred to avoid deadlock */ ++ schedule_work(&fitblk->remove_work); ++ } ++ mutex_unlock(&devices_mutex); ++} ++ ++static const struct blk_holder_ops fitblk_hops = { ++ .mark_dead = fitblk_mark_dead, ++}; ++ ++static int parse_fit_on_dev(struct device *dev) ++{ ++ struct file *bdev_file; ++ struct block_device *bdev; ++ struct address_space *mapping; ++ struct folio *folio; ++ pgoff_t f_index = 0; ++ size_t bytes_left, bytes_to_copy; ++ void *pre_fit, *fit, *fit_c; ++ u64 dsize, dsectors, imgmaxsect = 0; ++ u32 size, image_pos, image_len; ++ const __be32 *image_offset_be, *image_len_be, *image_pos_be; ++ int ret = 0, node, images, config; ++ const char *image_name, *image_type, *image_description, ++ *config_default, *config_description, *config_loadables; ++ u32 image_name_len, image_type_len, image_description_len, ++ bootconf_len, config_default_len, config_description_len, ++ config_loadables_len; ++ sector_t start_sect, nr_sects; ++ struct device_node *np = NULL; ++ const char *bootconf_c; ++ const char *loadable; ++ char *bootconf = NULL, *bootconf_term; ++ bool found; ++ int loadables_rem_len, loadable_len; ++ u16 loadcnt; ++ unsigned int slot = 0; ++ ++ /* Exclusive open the block device to receive holder notifications */ ++ bdev_file = bdev_file_open_by_dev(dev->devt, ++ BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES, ++ &_fitblk_claim_ptr, &fitblk_hops); ++ if (!bdev_file) ++ return -ENODEV; ++ ++ if (IS_ERR(bdev_file)) ++ return PTR_ERR(bdev_file); ++ ++ bdev = file_bdev(bdev_file); ++ mapping = bdev_file->f_mapping; ++ ++ /* map first page */ ++ folio = read_mapping_folio(mapping, f_index++, NULL); ++ if (IS_ERR(folio)) { ++ ret = PTR_ERR(folio); ++ goto out_blkdev; ++ } ++ pre_fit = folio_address(folio) + offset_in_folio(folio, 0); ++ ++ /* uImage.FIT is based on flattened device tree structure */ ++ if (fdt_check_header(pre_fit)) { ++ ret = -EINVAL; ++ folio_put(folio); ++ goto out_blkdev; ++ } ++ ++ size = fdt_totalsize(pre_fit); ++ ++ if (size > PAGE_SIZE * FIT_MAX_PAGES) { ++ ret = -EOPNOTSUPP; ++ folio_put(folio); ++ goto out_blkdev; ++ } ++ ++ /* acquire disk size */ ++ dsectors = bdev_nr_sectors(bdev); ++ dsize = dsectors << SECTOR_SHIFT; ++ ++ /* abort if FIT structure is larger than disk or partition size */ ++ if (size >= dsize) { ++ ret = -EFBIG; ++ folio_put(folio); ++ goto out_blkdev; ++ } ++ ++ fit = kmalloc(size, GFP_KERNEL); ++ if (!fit) { ++ ret = -ENOMEM; ++ folio_put(folio); ++ goto out_blkdev; ++ } ++ ++ bytes_left = size; ++ fit_c = fit; ++ while (bytes_left > 0) { ++ bytes_to_copy = min_t(size_t, bytes_left, ++ folio_size(folio) - offset_in_folio(folio, 0)); ++ memcpy(fit_c, pre_fit, bytes_to_copy); ++ fit_c += bytes_to_copy; ++ bytes_left -= bytes_to_copy; ++ if (bytes_left) { ++ folio_put(folio); ++ folio = read_mapping_folio(mapping, f_index++, NULL); ++ if (IS_ERR(folio)) { ++ ret = PTR_ERR(folio); ++ goto out_blkdev; ++ }; ++ pre_fit = folio_address(folio) + offset_in_folio(folio, 0); ++ } ++ } ++ folio_put(folio); ++ ++ /* set boot config node name U-Boot may have added to the device tree */ ++ np = of_find_node_by_path("/chosen"); ++ if (np) { ++ bootconf_c = of_get_property(np, "u-boot,bootconf", &bootconf_len); ++ if (bootconf_c && bootconf_len) ++ bootconf = kmemdup_nul(bootconf_c, bootconf_len, GFP_KERNEL); ++ } ++ ++ if (bootconf) { ++ bootconf_term = strchr(bootconf, '#'); ++ if (bootconf_term) ++ *bootconf_term = '\0'; ++ } ++ ++ /* find configuration path in uImage.FIT */ ++ config = fdt_path_offset(fit, FIT_CONFS_PATH); ++ if (config < 0) { ++ pr_err("FIT: Cannot find %s node: %d\n", ++ FIT_CONFS_PATH, config); ++ ret = -ENOENT; ++ goto out_bootconf; ++ } ++ ++ /* get default configuration node name */ ++ config_default = ++ fdt_getprop(fit, config, FIT_DEFAULT_PROP, &config_default_len); ++ ++ /* make sure we got either default or selected boot config node name */ ++ if (!config_default && !bootconf) { ++ pr_err("FIT: Cannot find default configuration\n"); ++ ret = -ENOENT; ++ goto out_bootconf; ++ } ++ ++ /* find selected boot config node, fallback on default config node */ ++ node = fdt_subnode_offset(fit, config, bootconf ?: config_default); ++ if (node < 0) { ++ pr_err("FIT: Cannot find %s node: %d\n", ++ bootconf ?: config_default, node); ++ ret = -ENOENT; ++ goto out_bootconf; ++ } ++ ++ pr_info("FIT: Detected U-Boot %s\n", ubootver); ++ ++ /* get selected configuration data */ ++ config_description = ++ fdt_getprop(fit, node, FIT_DESC_PROP, &config_description_len); ++ config_loadables = fdt_getprop(fit, node, FIT_LOADABLE_PROP, ++ &config_loadables_len); ++ ++ pr_info("FIT: %s configuration: \"%.*s\"%s%.*s%s\n", ++ bootconf ? "Selected" : "Default", ++ bootconf ? bootconf_len : config_default_len, ++ bootconf ?: config_default, ++ config_description ? " (" : "", ++ config_description ? config_description_len : 0, ++ config_description ?: "", ++ config_description ? ")" : ""); ++ ++ if (!config_loadables || !config_loadables_len) { ++ pr_err("FIT: No loadables configured in \"%s\"\n", ++ bootconf ?: config_default); ++ ret = -ENOENT; ++ goto out_bootconf; ++ } ++ ++ /* get images path in uImage.FIT */ ++ images = fdt_path_offset(fit, FIT_IMAGES_PATH); ++ if (images < 0) { ++ pr_err("FIT: Cannot find %s node: %d\n", FIT_IMAGES_PATH, images); ++ ret = -EINVAL; ++ goto out_bootconf; ++ } ++ ++ /* iterate over images in uImage.FIT */ ++ fdt_for_each_subnode(node, fit, images) { ++ image_name = fdt_get_name(fit, node, &image_name_len); ++ image_type = fdt_getprop(fit, node, FIT_TYPE_PROP, &image_type_len); ++ image_offset_be = fdt_getprop(fit, node, FIT_DATA_OFFSET_PROP, NULL); ++ image_pos_be = fdt_getprop(fit, node, FIT_DATA_POSITION_PROP, NULL); ++ image_len_be = fdt_getprop(fit, node, FIT_DATA_SIZE_PROP, NULL); ++ ++ if (!image_name || !image_type || !image_len_be || ++ !image_name_len || !image_type_len) ++ continue; ++ ++ image_len = be32_to_cpu(*image_len_be); ++ if (!image_len) ++ continue; ++ ++ if (image_offset_be) ++ image_pos = be32_to_cpu(*image_offset_be) + size; ++ else if (image_pos_be) ++ image_pos = be32_to_cpu(*image_pos_be); ++ else ++ continue; ++ ++ image_description = fdt_getprop(fit, node, FIT_DESC_PROP, ++ &image_description_len); ++ ++ pr_info("FIT: %16s sub-image 0x%08x..0x%08x \"%.*s\"%s%.*s%s\n", ++ image_type, image_pos, image_pos + image_len - 1, ++ image_name_len, image_name, image_description ? " (" : "", ++ image_description ? image_description_len : 0, ++ image_description ?: "", image_description ? ") " : ""); ++ ++ /* only 'filesystem' images should be mapped as partitions */ ++ if (strncmp(image_type, FIT_FILESYSTEM_PROP, image_type_len)) ++ continue; ++ ++ /* check if sub-image is part of configured loadables */ ++ found = false; ++ loadable = config_loadables; ++ loadables_rem_len = config_loadables_len; ++ for (loadcnt = 0; loadables_rem_len > 1 && ++ loadcnt < MAX_FIT_LOADABLES; ++loadcnt) { ++ loadable_len = ++ strnlen(loadable, loadables_rem_len - 1) + 1; ++ loadables_rem_len -= loadable_len; ++ if (!strncmp(image_name, loadable, loadable_len)) { ++ found = true; ++ break; ++ } ++ loadable += loadable_len; ++ } ++ if (!found) ++ continue; ++ ++ if (image_pos % (1 << PAGE_SHIFT)) { ++ dev_err(dev, "FIT: image %.*s start not aligned to page boundaries, skipping\n", ++ image_name_len, image_name); ++ continue; ++ } ++ ++ if (image_len % (1 << PAGE_SHIFT)) { ++ dev_err(dev, "FIT: sub-image %.*s end not aligned to page boundaries, skipping\n", ++ image_name_len, image_name); ++ continue; ++ } ++ ++ start_sect = image_pos >> SECTOR_SHIFT; ++ nr_sects = image_len >> SECTOR_SHIFT; ++ imgmaxsect = max_t(sector_t, imgmaxsect, start_sect + nr_sects); ++ ++ if (start_sect + nr_sects > dsectors) { ++ dev_err(dev, "FIT: sub-image %.*s disk access beyond EOD\n", ++ image_name_len, image_name); ++ continue; ++ } ++ ++ if (!slot) { ++ ret = sysfs_create_link_nowarn(&pdev->dev.kobj, bdev_kobj(bdev), "lower_dev"); ++ if (ret && ret != -EEXIST) ++ goto out_bootconf; ++ ++ ret = 0; ++ } ++ ++ add_fit_subimage_device(bdev_file, slot++, start_sect, nr_sects, true); ++ } ++ ++ if (!slot) ++ goto out_bootconf; ++ ++ dev_info(dev, "mapped %u uImage.FIT filesystem sub-image%s as /dev/fit%s%u%s\n", ++ slot, (slot > 1)?"s":"", (slot > 1)?"[0...":"", slot - 1, ++ (slot > 1)?"]":""); ++ ++ /* in case uImage.FIT is stored in a partition, map the remaining space */ ++ if (!bdev_read_only(bdev) && bdev_is_partition(bdev) && ++ (imgmaxsect + MIN_FREE_SECT) < dsectors) { ++ add_fit_subimage_device(bdev_file, slot++, imgmaxsect, ++ dsectors - imgmaxsect, false); ++ dev_info(dev, "mapped remaining space as /dev/fitrw\n"); ++ } ++ ++out_bootconf: ++ kfree(bootconf); ++ kfree(fit); ++out_blkdev: ++ if (!slot) ++ fput(bdev_file); ++ ++ return ret; ++} ++ ++static int fitblk_match_of_node(struct device *dev, const void *np) ++{ ++ int ret; ++ ++ ret = device_match_of_node(dev, np); ++ if (ret) ++ return ret; ++ ++ /* ++ * To match ubiblock and mtdblock devices by their parent ubi ++ * or mtd device, also consider block device parent ++ */ ++ if (!dev->parent) ++ return 0; ++ ++ return device_match_of_node(dev->parent, np); ++} ++ ++static int fitblk_probe(struct platform_device *pdev) ++{ ++ struct device *dev; ++ ++ dev = class_find_device(&block_class, NULL, rootdisk, fitblk_match_of_node); ++ if (!dev) ++ return -EPROBE_DEFER; ++ ++ return parse_fit_on_dev(dev); ++} ++ ++static struct platform_driver fitblk_driver = { ++ .probe = fitblk_probe, ++ .driver = { ++ .name = "fitblk", ++ }, ++}; ++ ++static int __init fitblk_init(void) ++{ ++ /* detect U-Boot firmware */ ++ ubootver = of_get_property(of_chosen, "u-boot,version", NULL); ++ if (!ubootver) ++ return 0; ++ ++ /* parse 'rootdisk' property phandle */ ++ rootdisk = of_parse_phandle(of_chosen, "rootdisk", 0); ++ if (!rootdisk) ++ return 0; ++ ++ if (platform_driver_register(&fitblk_driver)) ++ return -ENODEV; ++ ++ refcount_set(&num_devs, 1); ++ pdev = platform_device_register_simple("fitblk", -1, NULL, 0); ++ if (IS_ERR(pdev)) ++ return PTR_ERR(pdev); ++ ++ return 0; ++} ++device_initcall(fitblk_init); +--- /dev/null ++++ b/include/uapi/linux/fitblk.h +@@ -0,0 +1,10 @@ ++/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ ++#ifndef _UAPI_LINUX_FITBLK_H ++#define _UAPI_LINUX_FITBLK_H ++ ++/* ++ * IOCTL commands --- we will commandeer 0x46 ('F') ++ */ ++#define FITBLK_RELEASE 0x4600 ++ ++#endif /* _UAPI_LINUX_FITBLK_H */ diff --git a/target/linux/generic/pending-6.12/511-init-bypass-device-lookup-for-dev-fit-rootfs.patch b/target/linux/generic/pending-6.12/511-init-bypass-device-lookup-for-dev-fit-rootfs.patch new file mode 100644 index 00000000000..b3d8d7dd0c6 --- /dev/null +++ b/target/linux/generic/pending-6.12/511-init-bypass-device-lookup-for-dev-fit-rootfs.patch @@ -0,0 +1,25 @@ +From 5ede3f8aed9a1a579bf7304142600d1f3500add9 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 12 Jun 2023 03:58:42 +0100 +Subject: [PATCH 2/2] init: bypass device lookup for /dev/fit* rootfs + +Allow 'rootwait' as /dev/fit* can show up late if the underlaying +device is probed late. + +Signed-off-by: Daniel Golle +--- + init/do_mounts.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -465,7 +465,8 @@ static dev_t __init parse_root_device(ch + int error; + dev_t dev; + +- if (!strncmp(root_device_name, "mtd", 3) || ++ if (!strncmp(root_device_name, "fit", 3) || ++ !strncmp(root_device_name, "mtd", 3) || + !strncmp(root_device_name, "ubi", 3)) + return Root_Generic; + if (strcmp(root_device_name, "/dev/nfs") == 0) diff --git a/target/linux/generic/pending-6.12/530-jffs2_make_lzma_available.patch b/target/linux/generic/pending-6.12/530-jffs2_make_lzma_available.patch new file mode 100644 index 00000000000..213e6f1f245 --- /dev/null +++ b/target/linux/generic/pending-6.12/530-jffs2_make_lzma_available.patch @@ -0,0 +1,5190 @@ +From: Alexandros C. Couloumbis +Subject: fs: add jffs2/lzma support (not activated by default yet) + +lede-commit: c2c88d315fa0e881f8b19da07b62859b915b11b2 +Signed-off-by: Alexandros C. Couloumbis +--- + fs/jffs2/Kconfig | 9 + + fs/jffs2/Makefile | 3 + + fs/jffs2/compr.c | 6 + + fs/jffs2/compr.h | 10 +- + fs/jffs2/compr_lzma.c | 128 +++ + fs/jffs2/super.c | 33 +- + include/linux/lzma.h | 62 ++ + include/linux/lzma/LzFind.h | 115 +++ + include/linux/lzma/LzHash.h | 54 + + include/linux/lzma/LzmaDec.h | 231 +++++ + include/linux/lzma/LzmaEnc.h | 80 ++ + include/linux/lzma/Types.h | 226 +++++ + include/uapi/linux/jffs2.h | 1 + + lib/Kconfig | 6 + + lib/Makefile | 12 + + lib/lzma/LzFind.c | 761 ++++++++++++++ + lib/lzma/LzmaDec.c | 999 +++++++++++++++++++ + lib/lzma/LzmaEnc.c | 2271 ++++++++++++++++++++++++++++++++++++++++++ + lib/lzma/Makefile | 7 + + 19 files changed, 5008 insertions(+), 6 deletions(-) + create mode 100644 fs/jffs2/compr_lzma.c + create mode 100644 include/linux/lzma.h + create mode 100644 include/linux/lzma/LzFind.h + create mode 100644 include/linux/lzma/LzHash.h + create mode 100644 include/linux/lzma/LzmaDec.h + create mode 100644 include/linux/lzma/LzmaEnc.h + create mode 100644 include/linux/lzma/Types.h + create mode 100644 lib/lzma/LzFind.c + create mode 100644 lib/lzma/LzmaDec.c + create mode 100644 lib/lzma/LzmaEnc.c + create mode 100644 lib/lzma/Makefile + +--- a/fs/jffs2/Kconfig ++++ b/fs/jffs2/Kconfig +@@ -136,6 +136,15 @@ config JFFS2_LZO + This feature was added in July, 2007. Say 'N' if you need + compatibility with older bootloaders or kernels. + ++config JFFS2_LZMA ++ bool "JFFS2 LZMA compression support" if JFFS2_COMPRESSION_OPTIONS ++ select LZMA_COMPRESS ++ select LZMA_DECOMPRESS ++ depends on JFFS2_FS ++ default n ++ help ++ JFFS2 wrapper to the LZMA C SDK ++ + config JFFS2_RTIME + bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS +--- a/fs/jffs2/Makefile ++++ b/fs/jffs2/Makefile +@@ -19,4 +19,7 @@ jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rub + jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o + jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o + jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o ++jffs2-$(CONFIG_JFFS2_LZMA) += compr_lzma.o + jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o ++ ++CFLAGS_compr_lzma.o += -Iinclude/linux -Ilib/lzma +--- a/fs/jffs2/compr.c ++++ b/fs/jffs2/compr.c +@@ -381,6 +381,9 @@ int __init jffs2_compressors_init(void) + ret = jffs2_lzo_init(); + if (ret) + goto exit_dynrubin; ++ ret = jffs2_lzma_init(); ++ if (ret) ++ goto exit_lzo; + + + /* Setting default compression mode */ +@@ -402,6 +405,8 @@ int __init jffs2_compressors_init(void) + #endif + return 0; + ++exit_lzo: ++ jffs2_lzo_exit(); + exit_dynrubin: + jffs2_dynrubin_exit(); + exit_runinmips: +@@ -417,6 +422,7 @@ exit: + int jffs2_compressors_exit(void) + { + /* Unregistering compressors */ ++ jffs2_lzma_exit(); + jffs2_lzo_exit(); + jffs2_dynrubin_exit(); + jffs2_rubinmips_exit(); +--- a/fs/jffs2/compr.h ++++ b/fs/jffs2/compr.h +@@ -29,9 +29,9 @@ + #define JFFS2_DYNRUBIN_PRIORITY 20 + #define JFFS2_LZARI_PRIORITY 30 + #define JFFS2_RTIME_PRIORITY 50 +-#define JFFS2_ZLIB_PRIORITY 60 +-#define JFFS2_LZO_PRIORITY 80 +- ++#define JFFS2_LZMA_PRIORITY 70 ++#define JFFS2_ZLIB_PRIORITY 80 ++#define JFFS2_LZO_PRIORITY 90 + + #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ + #define JFFS2_DYNRUBIN_DISABLED /* for decompression */ +@@ -115,5 +115,12 @@ extern void jffs2_lzo_exit(void); + static inline int jffs2_lzo_init(void) { return 0; } + static inline void jffs2_lzo_exit(void) {} + #endif ++#ifdef CONFIG_JFFS2_LZMA ++extern int jffs2_lzma_init(void); ++extern void jffs2_lzma_exit(void); ++#else ++static inline int jffs2_lzma_init(void) { return 0; } ++static inline void jffs2_lzma_exit(void) {} ++#endif + + #endif /* __JFFS2_COMPR_H__ */ +--- /dev/null ++++ b/fs/jffs2/compr_lzma.c +@@ -0,0 +1,128 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * JFFS2 wrapper to the LZMA C SDK ++ * ++ */ ++ ++#include ++#include "compr.h" ++ ++#ifdef __KERNEL__ ++ static DEFINE_MUTEX(deflate_mutex); ++#endif ++ ++CLzmaEncHandle *p; ++Byte propsEncoded[LZMA_PROPS_SIZE]; ++SizeT propsSize = sizeof(propsEncoded); ++ ++STATIC void lzma_free_workspace(void) ++{ ++ LzmaEnc_Destroy(p, &lzma_alloc, &lzma_alloc); ++} ++ ++STATIC int INIT lzma_alloc_workspace(CLzmaEncProps *props) ++{ ++ if ((p = (CLzmaEncHandle *)LzmaEnc_Create(&lzma_alloc)) == NULL) ++ { ++ PRINT_ERROR("Failed to allocate lzma deflate workspace\n"); ++ return -ENOMEM; ++ } ++ ++ if (LzmaEnc_SetProps(p, props) != SZ_OK) ++ { ++ lzma_free_workspace(); ++ return -1; ++ } ++ ++ if (LzmaEnc_WriteProperties(p, propsEncoded, &propsSize) != SZ_OK) ++ { ++ lzma_free_workspace(); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++STATIC int jffs2_lzma_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) ++{ ++ SizeT compress_size = (SizeT)(*dstlen); ++ int ret; ++ ++ #ifdef __KERNEL__ ++ mutex_lock(&deflate_mutex); ++ #endif ++ ++ ret = LzmaEnc_MemEncode(p, cpage_out, &compress_size, data_in, *sourcelen, ++ 0, NULL, &lzma_alloc, &lzma_alloc); ++ ++ #ifdef __KERNEL__ ++ mutex_unlock(&deflate_mutex); ++ #endif ++ ++ if (ret != SZ_OK) ++ return -1; ++ ++ *dstlen = (uint32_t)compress_size; ++ ++ return 0; ++} ++ ++STATIC int jffs2_lzma_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen) ++{ ++ int ret; ++ SizeT dl = (SizeT)destlen; ++ SizeT sl = (SizeT)srclen; ++ ELzmaStatus status; ++ ++ ret = LzmaDecode(cpage_out, &dl, data_in, &sl, propsEncoded, ++ propsSize, LZMA_FINISH_ANY, &status, &lzma_alloc); ++ ++ if (ret != SZ_OK || status == LZMA_STATUS_NOT_FINISHED || dl != (SizeT)destlen) ++ return -1; ++ ++ return 0; ++} ++ ++static struct jffs2_compressor jffs2_lzma_comp = { ++ .priority = JFFS2_LZMA_PRIORITY, ++ .name = "lzma", ++ .compr = JFFS2_COMPR_LZMA, ++ .compress = &jffs2_lzma_compress, ++ .decompress = &jffs2_lzma_decompress, ++ .disabled = 0, ++}; ++ ++int INIT jffs2_lzma_init(void) ++{ ++ int ret; ++ CLzmaEncProps props; ++ LzmaEncProps_Init(&props); ++ ++ props.dictSize = LZMA_BEST_DICT(0x2000); ++ props.level = LZMA_BEST_LEVEL; ++ props.lc = LZMA_BEST_LC; ++ props.lp = LZMA_BEST_LP; ++ props.pb = LZMA_BEST_PB; ++ props.fb = LZMA_BEST_FB; ++ ++ ret = lzma_alloc_workspace(&props); ++ if (ret < 0) ++ return ret; ++ ++ ret = jffs2_register_compressor(&jffs2_lzma_comp); ++ if (ret) ++ lzma_free_workspace(); ++ ++ return ret; ++} ++ ++void jffs2_lzma_exit(void) ++{ ++ jffs2_unregister_compressor(&jffs2_lzma_comp); ++ lzma_free_workspace(); ++} +--- a/fs/jffs2/super.c ++++ b/fs/jffs2/super.c +@@ -376,14 +376,41 @@ static int __init init_jffs2_fs(void) + BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68); + BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32); + +- pr_info("version 2.2." ++ pr_info("version 2.2" + #ifdef CONFIG_JFFS2_FS_WRITEBUFFER + " (NAND)" + #endif + #ifdef CONFIG_JFFS2_SUMMARY +- " (SUMMARY) " ++ " (SUMMARY)" + #endif +- " © 2001-2006 Red Hat, Inc.\n"); ++#ifdef CONFIG_JFFS2_ZLIB ++ " (ZLIB)" ++#endif ++#ifdef CONFIG_JFFS2_LZO ++ " (LZO)" ++#endif ++#ifdef CONFIG_JFFS2_LZMA ++ " (LZMA)" ++#endif ++#ifdef CONFIG_JFFS2_RTIME ++ " (RTIME)" ++#endif ++#ifdef CONFIG_JFFS2_RUBIN ++ " (RUBIN)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_NONE ++ " (CMODE_NONE)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_PRIORITY ++ " (CMODE_PRIORITY)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_SIZE ++ " (CMODE_SIZE)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO ++ " (CMODE_FAVOURLZO)" ++#endif ++ " (c) 2001-2006 Red Hat, Inc.\n"); + + jffs2_inode_cachep = kmem_cache_create("jffs2_i", + sizeof(struct jffs2_inode_info), +--- /dev/null ++++ b/include/linux/lzma.h +@@ -0,0 +1,62 @@ ++#ifndef __LZMA_H__ ++#define __LZMA_H__ ++ ++#ifdef __KERNEL__ ++ #include ++ #include ++ #include ++ #include ++ #include ++ #define LZMA_MALLOC vmalloc ++ #define LZMA_FREE vfree ++ #define PRINT_ERROR(msg) printk(KERN_WARNING #msg) ++ #define INIT __init ++ #define STATIC static ++#else ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #ifndef PAGE_SIZE ++ extern int page_size; ++ #define PAGE_SIZE page_size ++ #endif ++ #define LZMA_MALLOC malloc ++ #define LZMA_FREE free ++ #define PRINT_ERROR(msg) fprintf(stderr, msg) ++ #define INIT ++ #define STATIC ++#endif ++ ++#include "lzma/LzmaDec.h" ++#include "lzma/LzmaEnc.h" ++ ++#define LZMA_BEST_LEVEL (9) ++#define LZMA_BEST_LC (0) ++#define LZMA_BEST_LP (0) ++#define LZMA_BEST_PB (0) ++#define LZMA_BEST_FB (273) ++ ++#define LZMA_BEST_DICT(n) (((int)((n) / 2)) * 2) ++ ++static void *p_lzma_malloc(void *p, size_t size) ++{ ++ if (size == 0) ++ return NULL; ++ ++ return LZMA_MALLOC(size); ++} ++ ++static void p_lzma_free(void *p, void *address) ++{ ++ if (address != NULL) ++ LZMA_FREE(address); ++} ++ ++static ISzAlloc lzma_alloc = { .Alloc = p_lzma_malloc, .Free = p_lzma_free }; ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzFind.h +@@ -0,0 +1,115 @@ ++/* LzFind.h -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZ_FIND_H ++#define __LZ_FIND_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++typedef UInt32 CLzRef; ++ ++typedef struct _CMatchFinder ++{ ++ Byte *buffer; ++ UInt32 pos; ++ UInt32 posLimit; ++ UInt32 streamPos; ++ UInt32 lenLimit; ++ ++ UInt32 cyclicBufferPos; ++ UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ ++ ++ UInt32 matchMaxLen; ++ CLzRef *hash; ++ CLzRef *son; ++ UInt32 hashMask; ++ UInt32 cutValue; ++ ++ Byte *bufferBase; ++ ISeqInStream *stream; ++ int streamEndWasReached; ++ ++ UInt32 blockSize; ++ UInt32 keepSizeBefore; ++ UInt32 keepSizeAfter; ++ ++ UInt32 numHashBytes; ++ int directInput; ++ size_t directInputRem; ++ int btMode; ++ int bigHash; ++ UInt32 historySize; ++ UInt32 fixedHashSize; ++ UInt32 hashSizeSum; ++ UInt32 numSons; ++ SRes result; ++ UInt32 crc[256]; ++} CMatchFinder; ++ ++#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) ++#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) ++ ++#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) ++ ++int MatchFinder_NeedMove(CMatchFinder *p); ++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); ++void MatchFinder_MoveBlock(CMatchFinder *p); ++void MatchFinder_ReadIfRequired(CMatchFinder *p); ++ ++void MatchFinder_Construct(CMatchFinder *p); ++ ++/* Conditions: ++ historySize <= 3 GB ++ keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB ++*/ ++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ++ ISzAlloc *alloc); ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); ++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); ++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); ++ ++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, ++ UInt32 *distances, UInt32 maxLen); ++ ++/* ++Conditions: ++ Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. ++ Mf_GetPointerToCurrentPos_Func's result must be used only before any other function ++*/ ++ ++typedef void (*Mf_Init_Func)(void *object); ++typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); ++typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); ++typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); ++typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); ++typedef void (*Mf_Skip_Func)(void *object, UInt32); ++ ++typedef struct _IMatchFinder ++{ ++ Mf_Init_Func Init; ++ Mf_GetIndexByte_Func GetIndexByte; ++ Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; ++ Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; ++ Mf_GetMatches_Func GetMatches; ++ Mf_Skip_Func Skip; ++} IMatchFinder; ++ ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); ++ ++void MatchFinder_Init(CMatchFinder *p); ++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); ++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); ++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); ++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzHash.h +@@ -0,0 +1,54 @@ ++/* LzHash.h -- HASH functions for LZ algorithms ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZ_HASH_H ++#define __LZ_HASH_H ++ ++#define kHash2Size (1 << 10) ++#define kHash3Size (1 << 16) ++#define kHash4Size (1 << 20) ++ ++#define kFix3HashSize (kHash2Size) ++#define kFix4HashSize (kHash2Size + kHash3Size) ++#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) ++ ++#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); ++ ++#define HASH3_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } ++ ++#define HASH4_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } ++ ++#define HASH5_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ ++ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ ++ hash4Value &= (kHash4Size - 1); } ++ ++/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ ++#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; ++ ++ ++#define MT_HASH2_CALC \ ++ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); ++ ++#define MT_HASH3_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } ++ ++#define MT_HASH4_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzmaDec.h +@@ -0,0 +1,231 @@ ++/* LzmaDec.h -- LZMA Decoder ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_DEC_H ++#define __LZMA_DEC_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* #define _LZMA_PROB32 */ ++/* _LZMA_PROB32 can increase the speed on some CPUs, ++ but memory usage for CLzmaDec::probs will be doubled in that case */ ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++ ++/* ---------- LZMA Properties ---------- */ ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaProps ++{ ++ unsigned lc, lp, pb; ++ UInt32 dicSize; ++} CLzmaProps; ++ ++/* LzmaProps_Decode - decodes properties ++Returns: ++ SZ_OK ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); ++ ++ ++/* ---------- LZMA Decoder state ---------- */ ++ ++/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. ++ Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ ++ ++#define LZMA_REQUIRED_INPUT_MAX 20 ++ ++typedef struct ++{ ++ CLzmaProps prop; ++ CLzmaProb *probs; ++ Byte *dic; ++ const Byte *buf; ++ UInt32 range, code; ++ SizeT dicPos; ++ SizeT dicBufSize; ++ UInt32 processedPos; ++ UInt32 checkDicSize; ++ unsigned state; ++ UInt32 reps[4]; ++ unsigned remainLen; ++ int needFlush; ++ int needInitState; ++ UInt32 numProbs; ++ unsigned tempBufSize; ++ Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; ++} CLzmaDec; ++ ++#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } ++ ++void LzmaDec_Init(CLzmaDec *p); ++ ++/* There are two types of LZMA streams: ++ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. ++ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ ++ ++typedef enum ++{ ++ LZMA_FINISH_ANY, /* finish at any point */ ++ LZMA_FINISH_END /* block must be finished at the end */ ++} ELzmaFinishMode; ++ ++/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! ++ ++ You must use LZMA_FINISH_END, when you know that current output buffer ++ covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. ++ ++ If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, ++ and output value of destLen will be less than output buffer size limit. ++ You can check status result also. ++ ++ You can use multiple checks to test data integrity after full decompression: ++ 1) Check Result and "status" variable. ++ 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. ++ 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. ++ You must use correct finish mode in that case. */ ++ ++typedef enum ++{ ++ LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ ++ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ ++ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ ++ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ ++} ELzmaStatus; ++ ++/* ELzmaStatus is used only as output value for function call */ ++ ++ ++/* ---------- Interfaces ---------- */ ++ ++/* There are 3 levels of interfaces: ++ 1) Dictionary Interface ++ 2) Buffer Interface ++ 3) One Call Interface ++ You can select any of these interfaces, but don't mix functions from different ++ groups for same object. */ ++ ++ ++/* There are two variants to allocate state for Dictionary Interface: ++ 1) LzmaDec_Allocate / LzmaDec_Free ++ 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs ++ You can use variant 2, if you set dictionary buffer manually. ++ For Buffer Interface you must always use variant 1. ++ ++LzmaDec_Allocate* can return: ++ SZ_OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); ++ ++SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); ++ ++/* ---------- Dictionary Interface ---------- */ ++ ++/* You can use it, if you want to eliminate the overhead for data copying from ++ dictionary to some other external buffer. ++ You must work with CLzmaDec variables directly in this interface. ++ ++ STEPS: ++ LzmaDec_Constr() ++ LzmaDec_Allocate() ++ for (each new stream) ++ { ++ LzmaDec_Init() ++ while (it needs more decompression) ++ { ++ LzmaDec_DecodeToDic() ++ use data from CLzmaDec::dic and update CLzmaDec::dicPos ++ } ++ } ++ LzmaDec_Free() ++*/ ++ ++/* LzmaDec_DecodeToDic ++ ++ The decoding to internal dictionary buffer (CLzmaDec::dic). ++ You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (dicLimit). ++ LZMA_FINISH_ANY - Decode just dicLimit bytes. ++ LZMA_FINISH_END - Stream must be finished after dicLimit. ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_NEEDS_MORE_INPUT ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++*/ ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- Buffer Interface ---------- */ ++ ++/* It's zlib-like interface. ++ See LzmaDec_DecodeToDic description for information about STEPS and return results, ++ but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need ++ to work with CLzmaDec variables manually. ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++*/ ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaDecode ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++ SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). ++*/ ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzmaEnc.h +@@ -0,0 +1,80 @@ ++/* LzmaEnc.h -- LZMA Encoder ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_ENC_H ++#define __LZMA_ENC_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaEncProps ++{ ++ int level; /* 0 <= level <= 9 */ ++ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version ++ (1 << 12) <= dictSize <= (1 << 30) for 64-bit version ++ default = (1 << 24) */ ++ int lc; /* 0 <= lc <= 8, default = 3 */ ++ int lp; /* 0 <= lp <= 4, default = 0 */ ++ int pb; /* 0 <= pb <= 4, default = 2 */ ++ int algo; /* 0 - fast, 1 - normal, default = 1 */ ++ int fb; /* 5 <= fb <= 273, default = 32 */ ++ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ ++ int numHashBytes; /* 2, 3 or 4, default = 4 */ ++ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ ++ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ ++ int numThreads; /* 1 or 2, default = 2 */ ++} CLzmaEncProps; ++ ++void LzmaEncProps_Init(CLzmaEncProps *p); ++void LzmaEncProps_Normalize(CLzmaEncProps *p); ++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); ++ ++ ++/* ---------- CLzmaEncHandle Interface ---------- */ ++ ++/* LzmaEnc_* functions can return the following exit codes: ++Returns: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater in props ++ SZ_ERROR_WRITE - Write callback error. ++ SZ_ERROR_PROGRESS - some break from progress callback ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++typedef void * CLzmaEncHandle; ++ ++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); ++SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); ++SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaEncode ++Return code: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater ++ SZ_ERROR_OUTPUT_EOF - output buffer overflow ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/Types.h +@@ -0,0 +1,226 @@ ++/* Types.h -- Basic types ++2009-11-23 : Igor Pavlov : Public domain */ ++ ++#ifndef __7Z_TYPES_H ++#define __7Z_TYPES_H ++ ++#include ++ ++#ifdef _WIN32 ++#include ++#endif ++ ++#ifndef EXTERN_C_BEGIN ++#ifdef __cplusplus ++#define EXTERN_C_BEGIN extern "C" { ++#define EXTERN_C_END } ++#else ++#define EXTERN_C_BEGIN ++#define EXTERN_C_END ++#endif ++#endif ++ ++EXTERN_C_BEGIN ++ ++#define SZ_OK 0 ++ ++#define SZ_ERROR_DATA 1 ++#define SZ_ERROR_MEM 2 ++#define SZ_ERROR_CRC 3 ++#define SZ_ERROR_UNSUPPORTED 4 ++#define SZ_ERROR_PARAM 5 ++#define SZ_ERROR_INPUT_EOF 6 ++#define SZ_ERROR_OUTPUT_EOF 7 ++#define SZ_ERROR_READ 8 ++#define SZ_ERROR_WRITE 9 ++#define SZ_ERROR_PROGRESS 10 ++#define SZ_ERROR_FAIL 11 ++#define SZ_ERROR_THREAD 12 ++ ++#define SZ_ERROR_ARCHIVE 16 ++#define SZ_ERROR_NO_ARCHIVE 17 ++ ++typedef int SRes; ++ ++#ifdef _WIN32 ++typedef DWORD WRes; ++#else ++typedef int WRes; ++#endif ++ ++#ifndef RINOK ++#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } ++#endif ++ ++typedef unsigned char Byte; ++typedef short Int16; ++typedef unsigned short UInt16; ++ ++#ifdef _LZMA_UINT32_IS_ULONG ++typedef long Int32; ++typedef unsigned long UInt32; ++#else ++typedef int Int32; ++typedef unsigned int UInt32; ++#endif ++ ++#ifdef _SZ_NO_INT_64 ++ ++/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. ++ NOTES: Some code will work incorrectly in that case! */ ++ ++typedef long Int64; ++typedef unsigned long UInt64; ++ ++#else ++ ++#if defined(_MSC_VER) || defined(__BORLANDC__) ++typedef __int64 Int64; ++typedef unsigned __int64 UInt64; ++#else ++typedef long long int Int64; ++typedef unsigned long long int UInt64; ++#endif ++ ++#endif ++ ++#ifdef _LZMA_NO_SYSTEM_SIZE_T ++typedef UInt32 SizeT; ++#else ++typedef size_t SizeT; ++#endif ++ ++typedef int Bool; ++#define True 1 ++#define False 0 ++ ++ ++#ifdef _WIN32 ++#define MY_STD_CALL __stdcall ++#else ++#define MY_STD_CALL ++#endif ++ ++#ifdef _MSC_VER ++ ++#if _MSC_VER >= 1300 ++#define MY_NO_INLINE __declspec(noinline) ++#else ++#define MY_NO_INLINE ++#endif ++ ++#define MY_CDECL __cdecl ++#define MY_FAST_CALL __fastcall ++ ++#else ++ ++#define MY_CDECL ++#define MY_FAST_CALL ++ ++#endif ++ ++ ++/* The following interfaces use first parameter as pointer to structure */ ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) < input(*size)) is allowed */ ++} ISeqInStream; ++ ++/* it can return SZ_ERROR_INPUT_EOF */ ++SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); ++SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); ++SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); ++ ++typedef struct ++{ ++ size_t (*Write)(void *p, const void *buf, size_t size); ++ /* Returns: result - the number of actually written bytes. ++ (result < size) means error */ ++} ISeqOutStream; ++ ++typedef enum ++{ ++ SZ_SEEK_SET = 0, ++ SZ_SEEK_CUR = 1, ++ SZ_SEEK_END = 2 ++} ESzSeek; ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ISeekInStream; ++ ++typedef struct ++{ ++ SRes (*Look)(void *p, void **buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) > input(*size)) is not allowed ++ (output(*size) < input(*size)) is allowed */ ++ SRes (*Skip)(void *p, size_t offset); ++ /* offset must be <= output(*size) of Look */ ++ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* reads directly (without buffer). It's same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ILookInStream; ++ ++SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); ++SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); ++ ++/* reads via ILookInStream::Read */ ++SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); ++SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); ++ ++#define LookToRead_BUF_SIZE (1 << 14) ++ ++typedef struct ++{ ++ ILookInStream s; ++ ISeekInStream *realStream; ++ size_t pos; ++ size_t size; ++ Byte buf[LookToRead_BUF_SIZE]; ++} CLookToRead; ++ ++void LookToRead_CreateVTable(CLookToRead *p, int lookahead); ++void LookToRead_Init(CLookToRead *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToLook; ++ ++void SecToLook_CreateVTable(CSecToLook *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToRead; ++ ++void SecToRead_CreateVTable(CSecToRead *p); ++ ++typedef struct ++{ ++ SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); ++ /* Returns: result. (result != SZ_OK) means break. ++ Value (UInt64)(Int64)-1 for size means unknown value. */ ++} ICompressProgress; ++ ++typedef struct ++{ ++ void *(*Alloc)(void *p, size_t size); ++ void (*Free)(void *p, void *address); /* address can be 0 */ ++} ISzAlloc; ++ ++#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) ++#define IAlloc_Free(p, a) (p)->Free((p), a) ++ ++EXTERN_C_END ++ ++#endif +--- a/include/uapi/linux/jffs2.h ++++ b/include/uapi/linux/jffs2.h +@@ -46,6 +46,7 @@ + #define JFFS2_COMPR_DYNRUBIN 0x05 + #define JFFS2_COMPR_ZLIB 0x06 + #define JFFS2_COMPR_LZO 0x07 ++#define JFFS2_COMPR_LZMA 0x08 + /* Compatibility flags. */ + #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ + #define JFFS2_NODE_ACCURATE 0x2000 +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -353,6 +353,12 @@ config ZSTD_DECOMPRESS + + source "lib/xz/Kconfig" + ++config LZMA_COMPRESS ++ tristate ++ ++config LZMA_DECOMPRESS ++ tristate ++ + # + # These all provide a common interface (hence the apparent duplication with + # ZLIB_INFLATE; DECOMPRESS_GZIP is just a wrapper.) +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -126,6 +126,16 @@ CFLAGS_kobject.o += -DDEBUG + CFLAGS_kobject_uevent.o += -DDEBUG + endif + ++ifdef CONFIG_JFFS2_ZLIB ++ CONFIG_ZLIB_INFLATE:=y ++ CONFIG_ZLIB_DEFLATE:=y ++endif ++ ++ifdef CONFIG_JFFS2_LZMA ++ CONFIG_LZMA_DECOMPRESS:=y ++ CONFIG_LZMA_COMPRESS:=y ++endif ++ + obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o + CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any) + +@@ -185,6 +195,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/ + obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/ + obj-$(CONFIG_XZ_DEC) += xz/ + obj-$(CONFIG_RAID6_PQ) += raid6/ ++obj-$(CONFIG_LZMA_COMPRESS) += lzma/ ++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma/ + + lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o + lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o +--- /dev/null ++++ b/lib/lzma/LzFind.c +@@ -0,0 +1,761 @@ ++/* LzFind.c -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#include ++ ++#include "LzFind.h" ++#include "LzHash.h" ++ ++#define kEmptyHashValue 0 ++#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) ++#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ ++#define kNormalizeMask (~(kNormalizeStepMin - 1)) ++#define kMaxHistorySize ((UInt32)3 << 30) ++ ++#define kStartMaxLen 3 ++ ++static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ if (!p->directInput) ++ { ++ alloc->Free(alloc, p->bufferBase); ++ p->bufferBase = 0; ++ } ++} ++ ++/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ ++ ++static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) ++{ ++ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; ++ if (p->directInput) ++ { ++ p->blockSize = blockSize; ++ return 1; ++ } ++ if (p->bufferBase == 0 || p->blockSize != blockSize) ++ { ++ LzInWindow_Free(p, alloc); ++ p->blockSize = blockSize; ++ p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); ++ } ++ return (p->bufferBase != 0); ++} ++ ++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } ++static Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } ++ ++static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } ++ ++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) ++{ ++ p->posLimit -= subValue; ++ p->pos -= subValue; ++ p->streamPos -= subValue; ++} ++ ++static void MatchFinder_ReadBlock(CMatchFinder *p) ++{ ++ if (p->streamEndWasReached || p->result != SZ_OK) ++ return; ++ if (p->directInput) ++ { ++ UInt32 curSize = 0xFFFFFFFF - p->streamPos; ++ if (curSize > p->directInputRem) ++ curSize = (UInt32)p->directInputRem; ++ p->directInputRem -= curSize; ++ p->streamPos += curSize; ++ if (p->directInputRem == 0) ++ p->streamEndWasReached = 1; ++ return; ++ } ++ for (;;) ++ { ++ Byte *dest = p->buffer + (p->streamPos - p->pos); ++ size_t size = (p->bufferBase + p->blockSize - dest); ++ if (size == 0) ++ return; ++ p->result = p->stream->Read(p->stream, dest, &size); ++ if (p->result != SZ_OK) ++ return; ++ if (size == 0) ++ { ++ p->streamEndWasReached = 1; ++ return; ++ } ++ p->streamPos += (UInt32)size; ++ if (p->streamPos - p->pos > p->keepSizeAfter) ++ return; ++ } ++} ++ ++void MatchFinder_MoveBlock(CMatchFinder *p) ++{ ++ memmove(p->bufferBase, ++ p->buffer - p->keepSizeBefore, ++ (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); ++ p->buffer = p->bufferBase + p->keepSizeBefore; ++} ++ ++int MatchFinder_NeedMove(CMatchFinder *p) ++{ ++ if (p->directInput) ++ return 0; ++ /* if (p->streamEndWasReached) return 0; */ ++ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); ++} ++ ++void MatchFinder_ReadIfRequired(CMatchFinder *p) ++{ ++ if (p->streamEndWasReached) ++ return; ++ if (p->keepSizeAfter >= p->streamPos - p->pos) ++ MatchFinder_ReadBlock(p); ++} ++ ++static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) ++{ ++ if (MatchFinder_NeedMove(p)) ++ MatchFinder_MoveBlock(p); ++ MatchFinder_ReadBlock(p); ++} ++ ++static void MatchFinder_SetDefaultSettings(CMatchFinder *p) ++{ ++ p->cutValue = 32; ++ p->btMode = 1; ++ p->numHashBytes = 4; ++ p->bigHash = 0; ++} ++ ++#define kCrcPoly 0xEDB88320 ++ ++void MatchFinder_Construct(CMatchFinder *p) ++{ ++ UInt32 i; ++ p->bufferBase = 0; ++ p->directInput = 0; ++ p->hash = 0; ++ MatchFinder_SetDefaultSettings(p); ++ ++ for (i = 0; i < 256; i++) ++ { ++ UInt32 r = i; ++ int j; ++ for (j = 0; j < 8; j++) ++ r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); ++ p->crc[i] = r; ++ } ++} ++ ++static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->hash); ++ p->hash = 0; ++} ++ ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ MatchFinder_FreeThisClassMemory(p, alloc); ++ LzInWindow_Free(p, alloc); ++} ++ ++static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) ++{ ++ size_t sizeInBytes = (size_t)num * sizeof(CLzRef); ++ if (sizeInBytes / sizeof(CLzRef) != num) ++ return 0; ++ return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); ++} ++ ++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ++ ISzAlloc *alloc) ++{ ++ UInt32 sizeReserv; ++ if (historySize > kMaxHistorySize) ++ { ++ MatchFinder_Free(p, alloc); ++ return 0; ++ } ++ sizeReserv = historySize >> 1; ++ if (historySize > ((UInt32)2 << 30)) ++ sizeReserv = historySize >> 2; ++ sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); ++ ++ p->keepSizeBefore = historySize + keepAddBufferBefore + 1; ++ p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; ++ /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ ++ if (LzInWindow_Create(p, sizeReserv, alloc)) ++ { ++ UInt32 newCyclicBufferSize = historySize + 1; ++ UInt32 hs; ++ p->matchMaxLen = matchMaxLen; ++ { ++ p->fixedHashSize = 0; ++ if (p->numHashBytes == 2) ++ hs = (1 << 16) - 1; ++ else ++ { ++ hs = historySize - 1; ++ hs |= (hs >> 1); ++ hs |= (hs >> 2); ++ hs |= (hs >> 4); ++ hs |= (hs >> 8); ++ hs >>= 1; ++ hs |= 0xFFFF; /* don't change it! It's required for Deflate */ ++ if (hs > (1 << 24)) ++ { ++ if (p->numHashBytes == 3) ++ hs = (1 << 24) - 1; ++ else ++ hs >>= 1; ++ } ++ } ++ p->hashMask = hs; ++ hs++; ++ if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; ++ if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; ++ if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; ++ hs += p->fixedHashSize; ++ } ++ ++ { ++ UInt32 prevSize = p->hashSizeSum + p->numSons; ++ UInt32 newSize; ++ p->historySize = historySize; ++ p->hashSizeSum = hs; ++ p->cyclicBufferSize = newCyclicBufferSize; ++ p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); ++ newSize = p->hashSizeSum + p->numSons; ++ if (p->hash != 0 && prevSize == newSize) ++ return 1; ++ MatchFinder_FreeThisClassMemory(p, alloc); ++ p->hash = AllocRefs(newSize, alloc); ++ if (p->hash != 0) ++ { ++ p->son = p->hash + p->hashSizeSum; ++ return 1; ++ } ++ } ++ } ++ MatchFinder_Free(p, alloc); ++ return 0; ++} ++ ++static void MatchFinder_SetLimits(CMatchFinder *p) ++{ ++ UInt32 limit = kMaxValForNormalize - p->pos; ++ UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; ++ if (limit2 < limit) ++ limit = limit2; ++ limit2 = p->streamPos - p->pos; ++ if (limit2 <= p->keepSizeAfter) ++ { ++ if (limit2 > 0) ++ limit2 = 1; ++ } ++ else ++ limit2 -= p->keepSizeAfter; ++ if (limit2 < limit) ++ limit = limit2; ++ { ++ UInt32 lenLimit = p->streamPos - p->pos; ++ if (lenLimit > p->matchMaxLen) ++ lenLimit = p->matchMaxLen; ++ p->lenLimit = lenLimit; ++ } ++ p->posLimit = p->pos + limit; ++} ++ ++void MatchFinder_Init(CMatchFinder *p) ++{ ++ UInt32 i; ++ for (i = 0; i < p->hashSizeSum; i++) ++ p->hash[i] = kEmptyHashValue; ++ p->cyclicBufferPos = 0; ++ p->buffer = p->bufferBase; ++ p->pos = p->streamPos = p->cyclicBufferSize; ++ p->result = SZ_OK; ++ p->streamEndWasReached = 0; ++ MatchFinder_ReadBlock(p); ++ MatchFinder_SetLimits(p); ++} ++ ++static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) ++{ ++ return (p->pos - p->historySize - 1) & kNormalizeMask; ++} ++ ++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) ++{ ++ UInt32 i; ++ for (i = 0; i < numItems; i++) ++ { ++ UInt32 value = items[i]; ++ if (value <= subValue) ++ value = kEmptyHashValue; ++ else ++ value -= subValue; ++ items[i] = value; ++ } ++} ++ ++static void MatchFinder_Normalize(CMatchFinder *p) ++{ ++ UInt32 subValue = MatchFinder_GetSubValue(p); ++ MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); ++ MatchFinder_ReduceOffsets(p, subValue); ++} ++ ++static void MatchFinder_CheckLimits(CMatchFinder *p) ++{ ++ if (p->pos == kMaxValForNormalize) ++ MatchFinder_Normalize(p); ++ if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) ++ MatchFinder_CheckAndMoveAndRead(p); ++ if (p->cyclicBufferPos == p->cyclicBufferSize) ++ p->cyclicBufferPos = 0; ++ MatchFinder_SetLimits(p); ++} ++ ++static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, ++ UInt32 *distances, UInt32 maxLen) ++{ ++ son[_cyclicBufferPos] = curMatch; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ return distances; ++ { ++ const Byte *pb = cur - delta; ++ curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; ++ if (pb[maxLen] == cur[maxLen] && *pb == *cur) ++ { ++ UInt32 len = 0; ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ if (maxLen < len) ++ { ++ *distances++ = maxLen = len; ++ *distances++ = delta - 1; ++ if (len == lenLimit) ++ return distances; ++ } ++ } ++ } ++ } ++} ++ ++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, ++ UInt32 *distances, UInt32 maxLen) ++{ ++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; ++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1); ++ UInt32 len0 = 0, len1 = 0; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ { ++ *ptr0 = *ptr1 = kEmptyHashValue; ++ return distances; ++ } ++ { ++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); ++ const Byte *pb = cur - delta; ++ UInt32 len = (len0 < len1 ? len0 : len1); ++ if (pb[len] == cur[len]) ++ { ++ if (++len != lenLimit && pb[len] == cur[len]) ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ if (maxLen < len) ++ { ++ *distances++ = maxLen = len; ++ *distances++ = delta - 1; ++ if (len == lenLimit) ++ { ++ *ptr1 = pair[0]; ++ *ptr0 = pair[1]; ++ return distances; ++ } ++ } ++ } ++ if (pb[len] < cur[len]) ++ { ++ *ptr1 = curMatch; ++ ptr1 = pair + 1; ++ curMatch = *ptr1; ++ len1 = len; ++ } ++ else ++ { ++ *ptr0 = curMatch; ++ ptr0 = pair; ++ curMatch = *ptr0; ++ len0 = len; ++ } ++ } ++ } ++} ++ ++static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) ++{ ++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; ++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1); ++ UInt32 len0 = 0, len1 = 0; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ { ++ *ptr0 = *ptr1 = kEmptyHashValue; ++ return; ++ } ++ { ++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); ++ const Byte *pb = cur - delta; ++ UInt32 len = (len0 < len1 ? len0 : len1); ++ if (pb[len] == cur[len]) ++ { ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ { ++ if (len == lenLimit) ++ { ++ *ptr1 = pair[0]; ++ *ptr0 = pair[1]; ++ return; ++ } ++ } ++ } ++ if (pb[len] < cur[len]) ++ { ++ *ptr1 = curMatch; ++ ptr1 = pair + 1; ++ curMatch = *ptr1; ++ len1 = len; ++ } ++ else ++ { ++ *ptr0 = curMatch; ++ ptr0 = pair; ++ curMatch = *ptr0; ++ len0 = len; ++ } ++ } ++ } ++} ++ ++#define MOVE_POS \ ++ ++p->cyclicBufferPos; \ ++ p->buffer++; \ ++ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); ++ ++#define MOVE_POS_RET MOVE_POS return offset; ++ ++static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } ++ ++#define GET_MATCHES_HEADER2(minLen, ret_op) \ ++ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ ++ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ ++ cur = p->buffer; ++ ++#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) ++#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) ++ ++#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue ++ ++#define GET_MATCHES_FOOTER(offset, maxLen) \ ++ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ ++ distances + offset, maxLen) - distances); MOVE_POS_RET; ++ ++#define SKIP_FOOTER \ ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; ++ ++static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(2) ++ HASH2_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = 0; ++ GET_MATCHES_FOOTER(offset, 1) ++} ++ ++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = 0; ++ GET_MATCHES_FOOTER(offset, 2) ++} ++ ++static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, delta2, maxLen, offset; ++ GET_MATCHES_HEADER(3) ++ ++ HASH3_CALC; ++ ++ delta2 = p->pos - p->hash[hash2Value]; ++ curMatch = p->hash[kFix3HashSize + hashValue]; ++ ++ p->hash[hash2Value] = ++ p->hash[kFix3HashSize + hashValue] = p->pos; ++ ++ ++ maxLen = 2; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[0] = maxLen; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ if (maxLen == lenLimit) ++ { ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); ++ MOVE_POS_RET; ++ } ++ } ++ GET_MATCHES_FOOTER(offset, maxLen) ++} ++ ++static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; ++ GET_MATCHES_HEADER(4) ++ ++ HASH4_CALC; ++ ++ delta2 = p->pos - p->hash[ hash2Value]; ++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ ++ maxLen = 1; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ distances[0] = maxLen = 2; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ } ++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) ++ { ++ maxLen = 3; ++ distances[offset + 1] = delta3 - 1; ++ offset += 2; ++ delta2 = delta3; ++ } ++ if (offset != 0) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[offset - 2] = maxLen; ++ if (maxLen == lenLimit) ++ { ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); ++ MOVE_POS_RET; ++ } ++ } ++ if (maxLen < 3) ++ maxLen = 3; ++ GET_MATCHES_FOOTER(offset, maxLen) ++} ++ ++static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; ++ GET_MATCHES_HEADER(4) ++ ++ HASH4_CALC; ++ ++ delta2 = p->pos - p->hash[ hash2Value]; ++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ ++ maxLen = 1; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ distances[0] = maxLen = 2; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ } ++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) ++ { ++ maxLen = 3; ++ distances[offset + 1] = delta3 - 1; ++ offset += 2; ++ delta2 = delta3; ++ } ++ if (offset != 0) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[offset - 2] = maxLen; ++ if (maxLen == lenLimit) ++ { ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS_RET; ++ } ++ } ++ if (maxLen < 3) ++ maxLen = 3; ++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), ++ distances + offset, maxLen) - (distances)); ++ MOVE_POS_RET ++} ++ ++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), ++ distances, 2) - (distances)); ++ MOVE_POS_RET ++} ++ ++static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(2) ++ HASH2_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value; ++ SKIP_HEADER(3) ++ HASH3_CALC; ++ curMatch = p->hash[kFix3HashSize + hashValue]; ++ p->hash[hash2Value] = ++ p->hash[kFix3HashSize + hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value, hash3Value; ++ SKIP_HEADER(4) ++ HASH4_CALC; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = p->pos; ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value, hash3Value; ++ SKIP_HEADER(4) ++ HASH4_CALC; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS ++ } ++ while (--num != 0); ++} ++ ++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS ++ } ++ while (--num != 0); ++} ++ ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) ++{ ++ vTable->Init = (Mf_Init_Func)MatchFinder_Init; ++ vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; ++ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; ++ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; ++ if (!p->btMode) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; ++ } ++ else if (p->numHashBytes == 2) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; ++ } ++ else if (p->numHashBytes == 3) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; ++ } ++ else ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; ++ } ++} +--- /dev/null ++++ b/lib/lzma/LzmaDec.c +@@ -0,0 +1,999 @@ ++/* LzmaDec.c -- LZMA Decoder ++2009-09-20 : Igor Pavlov : Public domain */ ++ ++#include "LzmaDec.h" ++ ++#include ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++ ++#define RC_INIT_SIZE 5 ++ ++#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); ++#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); ++#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ ++ { UPDATE_0(p); i = (i + i); A0; } else \ ++ { UPDATE_1(p); i = (i + i) + 1; A1; } ++#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) ++ ++#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } ++#define TREE_DECODE(probs, limit, i) \ ++ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } ++ ++/* #define _LZMA_SIZE_OPT */ ++ ++#ifdef _LZMA_SIZE_OPT ++#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) ++#else ++#define TREE_6_DECODE(probs, i) \ ++ { i = 1; \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ i -= 0x40; } ++#endif ++ ++#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0_CHECK range = bound; ++#define UPDATE_1_CHECK range -= bound; code -= bound; ++#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ ++ { UPDATE_0_CHECK; i = (i + i); A0; } else \ ++ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } ++#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) ++#define TREE_DECODE_CHECK(probs, limit, i) \ ++ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } ++ ++ ++#define kNumPosBitsMax 4 ++#define kNumPosStatesMax (1 << kNumPosBitsMax) ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define LenChoice 0 ++#define LenChoice2 (LenChoice + 1) ++#define LenLow (LenChoice2 + 1) ++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) ++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) ++#define kNumLenProbs (LenHigh + kLenNumHighSymbols) ++ ++ ++#define kNumStates 12 ++#define kNumLitStates 7 ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#define kNumPosSlotBits 6 ++#define kNumLenToPosStates 4 ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++ ++#define kMatchMinLen 2 ++#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define IsMatch 0 ++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) ++#define IsRepG0 (IsRep + kNumStates) ++#define IsRepG1 (IsRepG0 + kNumStates) ++#define IsRepG2 (IsRepG1 + kNumStates) ++#define IsRep0Long (IsRepG2 + kNumStates) ++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) ++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) ++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) ++#define LenCoder (Align + kAlignTableSize) ++#define RepLenCoder (LenCoder + kNumLenProbs) ++#define Literal (RepLenCoder + kNumLenProbs) ++ ++#define LZMA_BASE_SIZE 1846 ++#define LZMA_LIT_SIZE 768 ++ ++#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) ++ ++#if Literal != LZMA_BASE_SIZE ++StopCompilingDueBUG ++#endif ++ ++#define LZMA_DIC_MIN (1 << 12) ++ ++/* First LZMA-symbol is always decoded. ++And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization ++Out: ++ Result: ++ SZ_OK - OK ++ SZ_ERROR_DATA - Error ++ p->remainLen: ++ < kMatchSpecLenStart : normal remain ++ = kMatchSpecLenStart : finished ++ = kMatchSpecLenStart + 1 : Flush marker ++ = kMatchSpecLenStart + 2 : State Init Marker ++*/ ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ CLzmaProb *probs = p->probs; ++ ++ unsigned state = p->state; ++ UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; ++ unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; ++ unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; ++ unsigned lc = p->prop.lc; ++ ++ Byte *dic = p->dic; ++ SizeT dicBufSize = p->dicBufSize; ++ SizeT dicPos = p->dicPos; ++ ++ UInt32 processedPos = p->processedPos; ++ UInt32 checkDicSize = p->checkDicSize; ++ unsigned len = 0; ++ ++ const Byte *buf = p->buf; ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ ++ do ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = processedPos & pbMask; ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ unsigned symbol; ++ UPDATE_0(prob); ++ prob = probs + Literal; ++ if (checkDicSize != 0 || processedPos != 0) ++ prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + ++ (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ state -= (state < 4) ? state : 3; ++ symbol = 1; ++ do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ state -= (state < 10) ? 3 : 6; ++ symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ dic[dicPos++] = (Byte)symbol; ++ processedPos++; ++ continue; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRep + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ state += kNumStates; ++ prob = probs + LenCoder; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ if (checkDicSize == 0 && processedPos == 0) ++ return SZ_ERROR_DATA; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ processedPos++; ++ state = state < kNumLitStates ? 9 : 11; ++ continue; ++ } ++ UPDATE_1(prob); ++ } ++ else ++ { ++ UInt32 distance; ++ UPDATE_1(prob); ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep1; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep2; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ distance = rep3; ++ rep3 = rep2; ++ } ++ rep2 = rep1; ++ } ++ rep1 = rep0; ++ rep0 = distance; ++ } ++ state = state < kNumLitStates ? 8 : 11; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = (1 << kLenNumLowBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenChoice2; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = (1 << kLenNumMidBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = (1 << kLenNumHighBits); ++ } ++ } ++ TREE_DECODE(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state >= kNumStates) ++ { ++ UInt32 distance; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); ++ TREE_6_DECODE(prob, distance); ++ if (distance >= kStartPosModelIndex) ++ { ++ unsigned posSlot = (unsigned)distance; ++ int numDirectBits = (int)(((distance >> 1) - 1)); ++ distance = (2 | (distance & 1)); ++ if (posSlot < kEndPosModelIndex) ++ { ++ distance <<= numDirectBits; ++ prob = probs + SpecPos + distance - posSlot - 1; ++ { ++ UInt32 mask = 1; ++ unsigned i = 1; ++ do ++ { ++ GET_BIT2(prob + i, i, ; , distance |= mask); ++ mask <<= 1; ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE ++ range >>= 1; ++ ++ { ++ UInt32 t; ++ code -= range; ++ t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ ++ distance = (distance << 1) + (t + 1); ++ code += range & t; ++ } ++ /* ++ distance <<= 1; ++ if (code >= range) ++ { ++ code -= range; ++ distance |= 1; ++ } ++ */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ distance <<= kNumAlignBits; ++ { ++ unsigned i = 1; ++ GET_BIT2(prob + i, i, ; , distance |= 1); ++ GET_BIT2(prob + i, i, ; , distance |= 2); ++ GET_BIT2(prob + i, i, ; , distance |= 4); ++ GET_BIT2(prob + i, i, ; , distance |= 8); ++ } ++ if (distance == (UInt32)0xFFFFFFFF) ++ { ++ len += kMatchSpecLenStart; ++ state -= kNumStates; ++ break; ++ } ++ } ++ } ++ rep3 = rep2; ++ rep2 = rep1; ++ rep1 = rep0; ++ rep0 = distance + 1; ++ if (checkDicSize == 0) ++ { ++ if (distance >= processedPos) ++ return SZ_ERROR_DATA; ++ } ++ else if (distance >= checkDicSize) ++ return SZ_ERROR_DATA; ++ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; ++ } ++ ++ len += kMatchMinLen; ++ ++ if (limit == dicPos) ++ return SZ_ERROR_DATA; ++ { ++ SizeT rem = limit - dicPos; ++ unsigned curLen = ((rem < len) ? (unsigned)rem : len); ++ SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); ++ ++ processedPos += curLen; ++ ++ len -= curLen; ++ if (pos + curLen <= dicBufSize) ++ { ++ Byte *dest = dic + dicPos; ++ ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; ++ const Byte *lim = dest + curLen; ++ dicPos += curLen; ++ do ++ *(dest) = (Byte)*(dest + src); ++ while (++dest != lim); ++ } ++ else ++ { ++ do ++ { ++ dic[dicPos++] = dic[pos]; ++ if (++pos == dicBufSize) ++ pos = 0; ++ } ++ while (--curLen != 0); ++ } ++ } ++ } ++ } ++ while (dicPos < limit && buf < bufLimit); ++ NORMALIZE; ++ p->buf = buf; ++ p->range = range; ++ p->code = code; ++ p->remainLen = len; ++ p->dicPos = dicPos; ++ p->processedPos = processedPos; ++ p->reps[0] = rep0; ++ p->reps[1] = rep1; ++ p->reps[2] = rep2; ++ p->reps[3] = rep3; ++ p->state = state; ++ ++ return SZ_OK; ++} ++ ++static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) ++{ ++ if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) ++ { ++ Byte *dic = p->dic; ++ SizeT dicPos = p->dicPos; ++ SizeT dicBufSize = p->dicBufSize; ++ unsigned len = p->remainLen; ++ UInt32 rep0 = p->reps[0]; ++ if (limit - dicPos < len) ++ len = (unsigned)(limit - dicPos); ++ ++ if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) ++ p->checkDicSize = p->prop.dicSize; ++ ++ p->processedPos += len; ++ p->remainLen -= len; ++ while (len-- != 0) ++ { ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ } ++ p->dicPos = dicPos; ++ } ++} ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ do ++ { ++ SizeT limit2 = limit; ++ if (p->checkDicSize == 0) ++ { ++ UInt32 rem = p->prop.dicSize - p->processedPos; ++ if (limit - p->dicPos > rem) ++ limit2 = p->dicPos + rem; ++ } ++ RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); ++ if (p->processedPos >= p->prop.dicSize) ++ p->checkDicSize = p->prop.dicSize; ++ LzmaDec_WriteRem(p, limit); ++ } ++ while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); ++ ++ if (p->remainLen > kMatchSpecLenStart) ++ { ++ p->remainLen = kMatchSpecLenStart; ++ } ++ return 0; ++} ++ ++typedef enum ++{ ++ DUMMY_ERROR, /* unexpected end of input stream */ ++ DUMMY_LIT, ++ DUMMY_MATCH, ++ DUMMY_REP ++} ELzmaDummy; ++ ++static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) ++{ ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ const Byte *bufLimit = buf + inSize; ++ CLzmaProb *probs = p->probs; ++ unsigned state = p->state; ++ ELzmaDummy res; ++ ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK ++ ++ /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ ++ ++ prob = probs + Literal; ++ if (p->checkDicSize != 0 || p->processedPos != 0) ++ prob += (LZMA_LIT_SIZE * ++ ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + ++ (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ unsigned symbol = 1; ++ do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[p->dicPos - p->reps[0] + ++ ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ unsigned symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ res = DUMMY_LIT; ++ } ++ else ++ { ++ unsigned len; ++ UPDATE_1_CHECK; ++ ++ prob = probs + IsRep + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ state = 0; ++ prob = probs + LenCoder; ++ res = DUMMY_MATCH; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ res = DUMMY_REP; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ NORMALIZE_CHECK; ++ return DUMMY_REP; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ } ++ state = kNumStates; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = 1 << kLenNumLowBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenChoice2; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = 1 << kLenNumMidBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = 1 << kLenNumHighBits; ++ } ++ } ++ TREE_DECODE_CHECK(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state < 4) ++ { ++ unsigned posSlot; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << ++ kNumPosSlotBits); ++ TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); ++ if (posSlot >= kStartPosModelIndex) ++ { ++ int numDirectBits = ((posSlot >> 1) - 1); ++ ++ /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ ++ ++ if (posSlot < kEndPosModelIndex) ++ { ++ prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE_CHECK ++ range >>= 1; ++ code -= range & (((code - range) >> 31) - 1); ++ /* if (code >= range) code -= range; */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ numDirectBits = kNumAlignBits; ++ } ++ { ++ unsigned i = 1; ++ do ++ { ++ GET_BIT_CHECK(prob + i, i); ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ } ++ } ++ } ++ NORMALIZE_CHECK; ++ return res; ++} ++ ++ ++static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) ++{ ++ p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); ++ p->range = 0xFFFFFFFF; ++ p->needFlush = 0; ++} ++ ++static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) ++{ ++ p->needFlush = 1; ++ p->remainLen = 0; ++ p->tempBufSize = 0; ++ ++ if (initDic) ++ { ++ p->processedPos = 0; ++ p->checkDicSize = 0; ++ p->needInitState = 1; ++ } ++ if (initState) ++ p->needInitState = 1; ++} ++ ++void LzmaDec_Init(CLzmaDec *p) ++{ ++ p->dicPos = 0; ++ LzmaDec_InitDicAndState(p, True, True); ++} ++ ++static void LzmaDec_InitStateReal(CLzmaDec *p) ++{ ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); ++ UInt32 i; ++ CLzmaProb *probs = p->probs; ++ for (i = 0; i < numProbs; i++) ++ probs[i] = kBitModelTotal >> 1; ++ p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; ++ p->state = 0; ++ p->needInitState = 0; ++} ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ++ ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT inSize = *srcLen; ++ (*srcLen) = 0; ++ LzmaDec_WriteRem(p, dicLimit); ++ ++ *status = LZMA_STATUS_NOT_SPECIFIED; ++ ++ while (p->remainLen != kMatchSpecLenStart) ++ { ++ int checkEndMarkNow; ++ ++ if (p->needFlush != 0) ++ { ++ for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) ++ p->tempBuf[p->tempBufSize++] = *src++; ++ if (p->tempBufSize < RC_INIT_SIZE) ++ { ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (p->tempBuf[0] != 0) ++ return SZ_ERROR_DATA; ++ ++ LzmaDec_InitRc(p, p->tempBuf); ++ p->tempBufSize = 0; ++ } ++ ++ checkEndMarkNow = 0; ++ if (p->dicPos >= dicLimit) ++ { ++ if (p->remainLen == 0 && p->code == 0) ++ { ++ *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; ++ return SZ_OK; ++ } ++ if (finishMode == LZMA_FINISH_ANY) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_OK; ++ } ++ if (p->remainLen != 0) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ checkEndMarkNow = 1; ++ } ++ ++ if (p->needInitState) ++ LzmaDec_InitStateReal(p); ++ ++ if (p->tempBufSize == 0) ++ { ++ SizeT processed; ++ const Byte *bufLimit; ++ if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, src, inSize); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ memcpy(p->tempBuf, src, inSize); ++ p->tempBufSize = (unsigned)inSize; ++ (*srcLen) += inSize; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ bufLimit = src; ++ } ++ else ++ bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; ++ p->buf = src; ++ if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) ++ return SZ_ERROR_DATA; ++ processed = (SizeT)(p->buf - src); ++ (*srcLen) += processed; ++ src += processed; ++ inSize -= processed; ++ } ++ else ++ { ++ unsigned rem = p->tempBufSize, lookAhead = 0; ++ while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) ++ p->tempBuf[rem++] = src[lookAhead++]; ++ p->tempBufSize = rem; ++ if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ (*srcLen) += lookAhead; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ } ++ p->buf = p->tempBuf; ++ if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) ++ return SZ_ERROR_DATA; ++ lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); ++ (*srcLen) += lookAhead; ++ src += lookAhead; ++ inSize -= lookAhead; ++ p->tempBufSize = 0; ++ } ++ } ++ if (p->code == 0) ++ *status = LZMA_STATUS_FINISHED_WITH_MARK; ++ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; ++} ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT outSize = *destLen; ++ SizeT inSize = *srcLen; ++ *srcLen = *destLen = 0; ++ for (;;) ++ { ++ SizeT inSizeCur = inSize, outSizeCur, dicPos; ++ ELzmaFinishMode curFinishMode; ++ SRes res; ++ if (p->dicPos == p->dicBufSize) ++ p->dicPos = 0; ++ dicPos = p->dicPos; ++ if (outSize > p->dicBufSize - dicPos) ++ { ++ outSizeCur = p->dicBufSize; ++ curFinishMode = LZMA_FINISH_ANY; ++ } ++ else ++ { ++ outSizeCur = dicPos + outSize; ++ curFinishMode = finishMode; ++ } ++ ++ res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); ++ src += inSizeCur; ++ inSize -= inSizeCur; ++ *srcLen += inSizeCur; ++ outSizeCur = p->dicPos - dicPos; ++ memcpy(dest, p->dic + dicPos, outSizeCur); ++ dest += outSizeCur; ++ outSize -= outSizeCur; ++ *destLen += outSizeCur; ++ if (res != 0) ++ return res; ++ if (outSizeCur == 0 || outSize == 0) ++ return SZ_OK; ++ } ++} ++ ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->probs); ++ p->probs = 0; ++} ++ ++static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->dic); ++ p->dic = 0; ++} ++ ++void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ LzmaDec_FreeProbs(p, alloc); ++ LzmaDec_FreeDict(p, alloc); ++} ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) ++{ ++ UInt32 dicSize; ++ Byte d; ++ ++ if (size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_UNSUPPORTED; ++ else ++ dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); ++ ++ if (dicSize < LZMA_DIC_MIN) ++ dicSize = LZMA_DIC_MIN; ++ p->dicSize = dicSize; ++ ++ d = data[0]; ++ if (d >= (9 * 5 * 5)) ++ return SZ_ERROR_UNSUPPORTED; ++ ++ p->lc = d % 9; ++ d /= 9; ++ p->pb = d / 5; ++ p->lp = d % 5; ++ ++ return SZ_OK; ++} ++ ++static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) ++{ ++ UInt32 numProbs = LzmaProps_GetNumProbs(propNew); ++ if (p->probs == 0 || numProbs != p->numProbs) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); ++ p->numProbs = numProbs; ++ if (p->probs == 0) ++ return SZ_ERROR_MEM; ++ } ++ return SZ_OK; ++} ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ SizeT dicBufSize; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ dicBufSize = propNew.dicSize; ++ if (p->dic == 0 || dicBufSize != p->dicBufSize) ++ { ++ LzmaDec_FreeDict(p, alloc); ++ p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); ++ if (p->dic == 0) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ } ++ p->dicBufSize = dicBufSize; ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc) ++{ ++ CLzmaDec p; ++ SRes res; ++ SizeT inSize = *srcLen; ++ SizeT outSize = *destLen; ++ *srcLen = *destLen = 0; ++ if (inSize < RC_INIT_SIZE) ++ return SZ_ERROR_INPUT_EOF; ++ ++ LzmaDec_Construct(&p); ++ res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); ++ if (res != 0) ++ return res; ++ p.dic = dest; ++ p.dicBufSize = outSize; ++ ++ LzmaDec_Init(&p); ++ ++ *srcLen = inSize; ++ res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); ++ ++ if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) ++ res = SZ_ERROR_INPUT_EOF; ++ ++ (*destLen) = p.dicPos; ++ LzmaDec_FreeProbs(&p, alloc); ++ return res; ++} +--- /dev/null ++++ b/lib/lzma/LzmaEnc.c +@@ -0,0 +1,2271 @@ ++/* LzmaEnc.c -- LZMA Encoder ++2009-11-24 : Igor Pavlov : Public domain */ ++ ++#include ++ ++/* #define SHOW_STAT */ ++/* #define SHOW_STAT2 */ ++ ++#if defined(SHOW_STAT) || defined(SHOW_STAT2) ++#include ++#endif ++ ++#include "LzmaEnc.h" ++ ++/* disable MT */ ++#define _7ZIP_ST ++ ++#include "LzFind.h" ++#ifndef _7ZIP_ST ++#include "LzFindMt.h" ++#endif ++ ++#ifdef SHOW_STAT ++static int ttt = 0; ++#endif ++ ++#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) ++ ++#define kBlockSize (9 << 10) ++#define kUnpackBlockSize (1 << 18) ++#define kMatchArraySize (1 << 21) ++#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) ++ ++#define kNumMaxDirectBits (31) ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++#define kProbInitValue (kBitModelTotal >> 1) ++ ++#define kNumMoveReducingBits 4 ++#define kNumBitPriceShiftBits 4 ++#define kBitPrice (1 << kNumBitPriceShiftBits) ++ ++void LzmaEncProps_Init(CLzmaEncProps *p) ++{ ++ p->level = 5; ++ p->dictSize = p->mc = 0; ++ p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; ++ p->writeEndMark = 0; ++} ++ ++void LzmaEncProps_Normalize(CLzmaEncProps *p) ++{ ++ int level = p->level; ++ if (level < 0) level = 5; ++ p->level = level; ++ if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); ++ if (p->lc < 0) p->lc = 3; ++ if (p->lp < 0) p->lp = 0; ++ if (p->pb < 0) p->pb = 2; ++ if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); ++ if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); ++ if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); ++ if (p->numHashBytes < 0) p->numHashBytes = 4; ++ if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); ++ if (p->numThreads < 0) ++ p->numThreads = ++ #ifndef _7ZIP_ST ++ ((p->btMode && p->algo) ? 2 : 1); ++ #else ++ 1; ++ #endif ++} ++ ++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) ++{ ++ CLzmaEncProps props = *props2; ++ LzmaEncProps_Normalize(&props); ++ return props.dictSize; ++} ++ ++/* #define LZMA_LOG_BSR */ ++/* Define it for Intel's CPU */ ++ ++ ++#ifdef LZMA_LOG_BSR ++ ++#define kDicLogSizeMaxCompress 30 ++ ++#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } ++ ++UInt32 GetPosSlot1(UInt32 pos) ++{ ++ UInt32 res; ++ BSR2_RET(pos, res); ++ return res; ++} ++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } ++#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } ++ ++#else ++ ++#define kNumLogBits (9 + (int)sizeof(size_t) / 2) ++#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) ++ ++static void LzmaEnc_FastPosInit(Byte *g_FastPos) ++{ ++ int c = 2, slotFast; ++ g_FastPos[0] = 0; ++ g_FastPos[1] = 1; ++ ++ for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) ++ { ++ UInt32 k = (1 << ((slotFast >> 1) - 1)); ++ UInt32 j; ++ for (j = 0; j < k; j++, c++) ++ g_FastPos[c] = (Byte)slotFast; ++ } ++} ++ ++#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ ++ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ ++ res = p->g_FastPos[pos >> i] + (i * 2); } ++/* ++#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ ++ p->g_FastPos[pos >> 6] + 12 : \ ++ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } ++*/ ++ ++#define GetPosSlot1(pos) p->g_FastPos[pos] ++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } ++#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } ++ ++#endif ++ ++ ++#define LZMA_NUM_REPS 4 ++ ++typedef unsigned CState; ++ ++typedef struct ++{ ++ UInt32 price; ++ ++ CState state; ++ int prev1IsChar; ++ int prev2; ++ ++ UInt32 posPrev2; ++ UInt32 backPrev2; ++ ++ UInt32 posPrev; ++ UInt32 backPrev; ++ UInt32 backs[LZMA_NUM_REPS]; ++} COptimal; ++ ++#define kNumOpts (1 << 12) ++ ++#define kNumLenToPosStates 4 ++#define kNumPosSlotBits 6 ++#define kDicLogSizeMin 0 ++#define kDicLogSizeMax 32 ++#define kDistTableSizeMax (kDicLogSizeMax * 2) ++ ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++#define kAlignMask (kAlignTableSize - 1) ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) ++ ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++#define LZMA_PB_MAX 4 ++#define LZMA_LC_MAX 8 ++#define LZMA_LP_MAX 4 ++ ++#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) ++ ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define LZMA_MATCH_LEN_MIN 2 ++#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) ++ ++#define kNumStates 12 ++ ++typedef struct ++{ ++ CLzmaProb choice; ++ CLzmaProb choice2; ++ CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; ++ CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; ++ CLzmaProb high[kLenNumHighSymbols]; ++} CLenEnc; ++ ++typedef struct ++{ ++ CLenEnc p; ++ UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; ++ UInt32 tableSize; ++ UInt32 counters[LZMA_NUM_PB_STATES_MAX]; ++} CLenPriceEnc; ++ ++typedef struct ++{ ++ UInt32 range; ++ Byte cache; ++ UInt64 low; ++ UInt64 cacheSize; ++ Byte *buf; ++ Byte *bufLim; ++ Byte *bufBase; ++ ISeqOutStream *outStream; ++ UInt64 processed; ++ SRes res; ++} CRangeEnc; ++ ++typedef struct ++{ ++ CLzmaProb *litProbs; ++ ++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ CLzmaProb isRep[kNumStates]; ++ CLzmaProb isRepG0[kNumStates]; ++ CLzmaProb isRepG1[kNumStates]; ++ CLzmaProb isRepG2[kNumStates]; ++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ ++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; ++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; ++ CLzmaProb posAlignEncoder[1 << kNumAlignBits]; ++ ++ CLenPriceEnc lenEnc; ++ CLenPriceEnc repLenEnc; ++ ++ UInt32 reps[LZMA_NUM_REPS]; ++ UInt32 state; ++} CSaveState; ++ ++typedef struct ++{ ++ IMatchFinder matchFinder; ++ void *matchFinderObj; ++ ++ #ifndef _7ZIP_ST ++ Bool mtMode; ++ CMatchFinderMt matchFinderMt; ++ #endif ++ ++ CMatchFinder matchFinderBase; ++ ++ #ifndef _7ZIP_ST ++ Byte pad[128]; ++ #endif ++ ++ UInt32 optimumEndIndex; ++ UInt32 optimumCurrentIndex; ++ ++ UInt32 longestMatchLength; ++ UInt32 numPairs; ++ UInt32 numAvail; ++ COptimal opt[kNumOpts]; ++ ++ #ifndef LZMA_LOG_BSR ++ Byte g_FastPos[1 << kNumLogBits]; ++ #endif ++ ++ UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; ++ UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; ++ UInt32 numFastBytes; ++ UInt32 additionalOffset; ++ UInt32 reps[LZMA_NUM_REPS]; ++ UInt32 state; ++ ++ UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; ++ UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; ++ UInt32 alignPrices[kAlignTableSize]; ++ UInt32 alignPriceCount; ++ ++ UInt32 distTableSize; ++ ++ unsigned lc, lp, pb; ++ unsigned lpMask, pbMask; ++ ++ CLzmaProb *litProbs; ++ ++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ CLzmaProb isRep[kNumStates]; ++ CLzmaProb isRepG0[kNumStates]; ++ CLzmaProb isRepG1[kNumStates]; ++ CLzmaProb isRepG2[kNumStates]; ++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ ++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; ++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; ++ CLzmaProb posAlignEncoder[1 << kNumAlignBits]; ++ ++ CLenPriceEnc lenEnc; ++ CLenPriceEnc repLenEnc; ++ ++ unsigned lclp; ++ ++ Bool fastMode; ++ ++ CRangeEnc rc; ++ ++ Bool writeEndMark; ++ UInt64 nowPos64; ++ UInt32 matchPriceCount; ++ Bool finished; ++ Bool multiThread; ++ ++ SRes result; ++ UInt32 dictSize; ++ UInt32 matchFinderCycles; ++ ++ int needInit; ++ ++ CSaveState saveState; ++} CLzmaEnc; ++ ++/*void LzmaEnc_SaveState(CLzmaEncHandle pp) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ CSaveState *dest = &p->saveState; ++ int i; ++ dest->lenEnc = p->lenEnc; ++ dest->repLenEnc = p->repLenEnc; ++ dest->state = p->state; ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); ++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); ++ } ++ for (i = 0; i < kNumLenToPosStates; i++) ++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); ++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); ++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); ++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); ++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); ++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); ++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); ++ memcpy(dest->reps, p->reps, sizeof(p->reps)); ++ memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); ++}*/ ++ ++/*void LzmaEnc_RestoreState(CLzmaEncHandle pp) ++{ ++ CLzmaEnc *dest = (CLzmaEnc *)pp; ++ const CSaveState *p = &dest->saveState; ++ int i; ++ dest->lenEnc = p->lenEnc; ++ dest->repLenEnc = p->repLenEnc; ++ dest->state = p->state; ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); ++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); ++ } ++ for (i = 0; i < kNumLenToPosStates; i++) ++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); ++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); ++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); ++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); ++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); ++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); ++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); ++ memcpy(dest->reps, p->reps, sizeof(p->reps)); ++ memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); ++}*/ ++ ++SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ CLzmaEncProps props = *props2; ++ LzmaEncProps_Normalize(&props); ++ ++ if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || ++ props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30)) ++ return SZ_ERROR_PARAM; ++ p->dictSize = props.dictSize; ++ p->matchFinderCycles = props.mc; ++ { ++ unsigned fb = props.fb; ++ if (fb < 5) ++ fb = 5; ++ if (fb > LZMA_MATCH_LEN_MAX) ++ fb = LZMA_MATCH_LEN_MAX; ++ p->numFastBytes = fb; ++ } ++ p->lc = props.lc; ++ p->lp = props.lp; ++ p->pb = props.pb; ++ p->fastMode = (props.algo == 0); ++ p->matchFinderBase.btMode = props.btMode; ++ { ++ UInt32 numHashBytes = 4; ++ if (props.btMode) ++ { ++ if (props.numHashBytes < 2) ++ numHashBytes = 2; ++ else if (props.numHashBytes < 4) ++ numHashBytes = props.numHashBytes; ++ } ++ p->matchFinderBase.numHashBytes = numHashBytes; ++ } ++ ++ p->matchFinderBase.cutValue = props.mc; ++ ++ p->writeEndMark = props.writeEndMark; ++ ++ #ifndef _7ZIP_ST ++ /* ++ if (newMultiThread != _multiThread) ++ { ++ ReleaseMatchFinder(); ++ _multiThread = newMultiThread; ++ } ++ */ ++ p->multiThread = (props.numThreads > 1); ++ #endif ++ ++ return SZ_OK; ++} ++ ++static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; ++static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; ++static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; ++static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; ++ ++#define IsCharState(s) ((s) < 7) ++ ++#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) ++ ++#define kInfinityPrice (1 << 30) ++ ++static void RangeEnc_Construct(CRangeEnc *p) ++{ ++ p->outStream = 0; ++ p->bufBase = 0; ++} ++ ++#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) ++ ++#define RC_BUF_SIZE (1 << 16) ++static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) ++{ ++ if (p->bufBase == 0) ++ { ++ p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); ++ if (p->bufBase == 0) ++ return 0; ++ p->bufLim = p->bufBase + RC_BUF_SIZE; ++ } ++ return 1; ++} ++ ++static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->bufBase); ++ p->bufBase = 0; ++} ++ ++static void RangeEnc_Init(CRangeEnc *p) ++{ ++ /* Stream.Init(); */ ++ p->low = 0; ++ p->range = 0xFFFFFFFF; ++ p->cacheSize = 1; ++ p->cache = 0; ++ ++ p->buf = p->bufBase; ++ ++ p->processed = 0; ++ p->res = SZ_OK; ++} ++ ++static void RangeEnc_FlushStream(CRangeEnc *p) ++{ ++ size_t num; ++ if (p->res != SZ_OK) ++ return; ++ num = p->buf - p->bufBase; ++ if (num != p->outStream->Write(p->outStream, p->bufBase, num)) ++ p->res = SZ_ERROR_WRITE; ++ p->processed += num; ++ p->buf = p->bufBase; ++} ++ ++static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) ++{ ++ if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0) ++ { ++ Byte temp = p->cache; ++ do ++ { ++ Byte *buf = p->buf; ++ *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); ++ p->buf = buf; ++ if (buf == p->bufLim) ++ RangeEnc_FlushStream(p); ++ temp = 0xFF; ++ } ++ while (--p->cacheSize != 0); ++ p->cache = (Byte)((UInt32)p->low >> 24); ++ } ++ p->cacheSize++; ++ p->low = (UInt32)p->low << 8; ++} ++ ++static void RangeEnc_FlushData(CRangeEnc *p) ++{ ++ int i; ++ for (i = 0; i < 5; i++) ++ RangeEnc_ShiftLow(p); ++} ++ ++static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits) ++{ ++ do ++ { ++ p->range >>= 1; ++ p->low += p->range & (0 - ((value >> --numBits) & 1)); ++ if (p->range < kTopValue) ++ { ++ p->range <<= 8; ++ RangeEnc_ShiftLow(p); ++ } ++ } ++ while (numBits != 0); ++} ++ ++static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) ++{ ++ UInt32 ttt = *prob; ++ UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; ++ if (symbol == 0) ++ { ++ p->range = newBound; ++ ttt += (kBitModelTotal - ttt) >> kNumMoveBits; ++ } ++ else ++ { ++ p->low += newBound; ++ p->range -= newBound; ++ ttt -= ttt >> kNumMoveBits; ++ } ++ *prob = (CLzmaProb)ttt; ++ if (p->range < kTopValue) ++ { ++ p->range <<= 8; ++ RangeEnc_ShiftLow(p); ++ } ++} ++ ++static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) ++{ ++ symbol |= 0x100; ++ do ++ { ++ RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); ++ symbol <<= 1; ++ } ++ while (symbol < 0x10000); ++} ++ ++static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) ++{ ++ UInt32 offs = 0x100; ++ symbol |= 0x100; ++ do ++ { ++ matchByte <<= 1; ++ RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); ++ symbol <<= 1; ++ offs &= ~(matchByte ^ symbol); ++ } ++ while (symbol < 0x10000); ++} ++ ++static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) ++{ ++ UInt32 i; ++ for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) ++ { ++ const int kCyclesBits = kNumBitPriceShiftBits; ++ UInt32 w = i; ++ UInt32 bitCount = 0; ++ int j; ++ for (j = 0; j < kCyclesBits; j++) ++ { ++ w = w * w; ++ bitCount <<= 1; ++ while (w >= ((UInt32)1 << 16)) ++ { ++ w >>= 1; ++ bitCount++; ++ } ++ } ++ ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); ++ } ++} ++ ++ ++#define GET_PRICE(prob, symbol) \ ++ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; ++ ++#define GET_PRICEa(prob, symbol) \ ++ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; ++ ++#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] ++#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] ++ ++#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] ++#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] ++ ++static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ symbol |= 0x100; ++ do ++ { ++ price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); ++ symbol <<= 1; ++ } ++ while (symbol < 0x10000); ++ return price; ++} ++ ++static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ UInt32 offs = 0x100; ++ symbol |= 0x100; ++ do ++ { ++ matchByte <<= 1; ++ price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); ++ symbol <<= 1; ++ offs &= ~(matchByte ^ symbol); ++ } ++ while (symbol < 0x10000); ++ return price; ++} ++ ++ ++static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) ++{ ++ UInt32 m = 1; ++ int i; ++ for (i = numBitLevels; i != 0;) ++ { ++ UInt32 bit; ++ i--; ++ bit = (symbol >> i) & 1; ++ RangeEnc_EncodeBit(rc, probs + m, bit); ++ m = (m << 1) | bit; ++ } ++} ++ ++static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) ++{ ++ UInt32 m = 1; ++ int i; ++ for (i = 0; i < numBitLevels; i++) ++ { ++ UInt32 bit = symbol & 1; ++ RangeEnc_EncodeBit(rc, probs + m, bit); ++ m = (m << 1) | bit; ++ symbol >>= 1; ++ } ++} ++ ++static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ symbol |= (1 << numBitLevels); ++ while (symbol != 1) ++ { ++ price += GET_PRICEa(probs[symbol >> 1], symbol & 1); ++ symbol >>= 1; ++ } ++ return price; ++} ++ ++static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ UInt32 m = 1; ++ int i; ++ for (i = numBitLevels; i != 0; i--) ++ { ++ UInt32 bit = symbol & 1; ++ symbol >>= 1; ++ price += GET_PRICEa(probs[m], bit); ++ m = (m << 1) | bit; ++ } ++ return price; ++} ++ ++ ++static void LenEnc_Init(CLenEnc *p) ++{ ++ unsigned i; ++ p->choice = p->choice2 = kProbInitValue; ++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) ++ p->low[i] = kProbInitValue; ++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) ++ p->mid[i] = kProbInitValue; ++ for (i = 0; i < kLenNumHighSymbols; i++) ++ p->high[i] = kProbInitValue; ++} ++ ++static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) ++{ ++ if (symbol < kLenNumLowSymbols) ++ { ++ RangeEnc_EncodeBit(rc, &p->choice, 0); ++ RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); ++ } ++ else ++ { ++ RangeEnc_EncodeBit(rc, &p->choice, 1); ++ if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) ++ { ++ RangeEnc_EncodeBit(rc, &p->choice2, 0); ++ RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); ++ } ++ else ++ { ++ RangeEnc_EncodeBit(rc, &p->choice2, 1); ++ RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); ++ } ++ } ++} ++ ++static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) ++{ ++ UInt32 a0 = GET_PRICE_0a(p->choice); ++ UInt32 a1 = GET_PRICE_1a(p->choice); ++ UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); ++ UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); ++ UInt32 i = 0; ++ for (i = 0; i < kLenNumLowSymbols; i++) ++ { ++ if (i >= numSymbols) ++ return; ++ prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); ++ } ++ for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) ++ { ++ if (i >= numSymbols) ++ return; ++ prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); ++ } ++ for (; i < numSymbols; i++) ++ prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); ++} ++ ++static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) ++{ ++ LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); ++ p->counters[posState] = p->tableSize; ++} ++ ++static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) ++{ ++ UInt32 posState; ++ for (posState = 0; posState < numPosStates; posState++) ++ LenPriceEnc_UpdateTable(p, posState, ProbPrices); ++} ++ ++static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) ++{ ++ LenEnc_Encode(&p->p, rc, symbol, posState); ++ if (updatePrice) ++ if (--p->counters[posState] == 0) ++ LenPriceEnc_UpdateTable(p, posState, ProbPrices); ++} ++ ++ ++ ++ ++static void MovePos(CLzmaEnc *p, UInt32 num) ++{ ++ #ifdef SHOW_STAT ++ ttt += num; ++ printf("\n MovePos %d", num); ++ #endif ++ if (num != 0) ++ { ++ p->additionalOffset += num; ++ p->matchFinder.Skip(p->matchFinderObj, num); ++ } ++} ++ ++static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) ++{ ++ UInt32 lenRes = 0, numPairs; ++ p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); ++ numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); ++ #ifdef SHOW_STAT ++ printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); ++ ttt++; ++ { ++ UInt32 i; ++ for (i = 0; i < numPairs; i += 2) ++ printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); ++ } ++ #endif ++ if (numPairs > 0) ++ { ++ lenRes = p->matches[numPairs - 2]; ++ if (lenRes == p->numFastBytes) ++ { ++ const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ UInt32 distance = p->matches[numPairs - 1] + 1; ++ UInt32 numAvail = p->numAvail; ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ { ++ const Byte *pby2 = pby - distance; ++ for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); ++ } ++ } ++ } ++ p->additionalOffset++; ++ *numDistancePairsRes = numPairs; ++ return lenRes; ++} ++ ++ ++#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; ++#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; ++#define IsShortRep(p) ((p)->backPrev == 0) ++ ++static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) ++{ ++ return ++ GET_PRICE_0(p->isRepG0[state]) + ++ GET_PRICE_0(p->isRep0Long[state][posState]); ++} ++ ++static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) ++{ ++ UInt32 price; ++ if (repIndex == 0) ++ { ++ price = GET_PRICE_0(p->isRepG0[state]); ++ price += GET_PRICE_1(p->isRep0Long[state][posState]); ++ } ++ else ++ { ++ price = GET_PRICE_1(p->isRepG0[state]); ++ if (repIndex == 1) ++ price += GET_PRICE_0(p->isRepG1[state]); ++ else ++ { ++ price += GET_PRICE_1(p->isRepG1[state]); ++ price += GET_PRICE(p->isRepG2[state], repIndex - 2); ++ } ++ } ++ return price; ++} ++ ++static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) ++{ ++ return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + ++ GetPureRepPrice(p, repIndex, state, posState); ++} ++ ++static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) ++{ ++ UInt32 posMem = p->opt[cur].posPrev; ++ UInt32 backMem = p->opt[cur].backPrev; ++ p->optimumEndIndex = cur; ++ do ++ { ++ if (p->opt[cur].prev1IsChar) ++ { ++ MakeAsChar(&p->opt[posMem]) ++ p->opt[posMem].posPrev = posMem - 1; ++ if (p->opt[cur].prev2) ++ { ++ p->opt[posMem - 1].prev1IsChar = False; ++ p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; ++ p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; ++ } ++ } ++ { ++ UInt32 posPrev = posMem; ++ UInt32 backCur = backMem; ++ ++ backMem = p->opt[posPrev].backPrev; ++ posMem = p->opt[posPrev].posPrev; ++ ++ p->opt[posPrev].backPrev = backCur; ++ p->opt[posPrev].posPrev = cur; ++ cur = posPrev; ++ } ++ } ++ while (cur != 0); ++ *backRes = p->opt[0].backPrev; ++ p->optimumCurrentIndex = p->opt[0].posPrev; ++ return p->optimumCurrentIndex; ++} ++ ++#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) ++ ++static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) ++{ ++ UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; ++ UInt32 matchPrice, repMatchPrice, normalMatchPrice; ++ UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; ++ UInt32 *matches; ++ const Byte *data; ++ Byte curByte, matchByte; ++ if (p->optimumEndIndex != p->optimumCurrentIndex) ++ { ++ const COptimal *opt = &p->opt[p->optimumCurrentIndex]; ++ UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; ++ *backRes = opt->backPrev; ++ p->optimumCurrentIndex = opt->posPrev; ++ return lenRes; ++ } ++ p->optimumCurrentIndex = p->optimumEndIndex = 0; ++ ++ if (p->additionalOffset == 0) ++ mainLen = ReadMatchDistances(p, &numPairs); ++ else ++ { ++ mainLen = p->longestMatchLength; ++ numPairs = p->numPairs; ++ } ++ ++ numAvail = p->numAvail; ++ if (numAvail < 2) ++ { ++ *backRes = (UInt32)(-1); ++ return 1; ++ } ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ repMaxIndex = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 lenTest; ++ const Byte *data2; ++ reps[i] = p->reps[i]; ++ data2 = data - (reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ { ++ repLens[i] = 0; ++ continue; ++ } ++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); ++ repLens[i] = lenTest; ++ if (lenTest > repLens[repMaxIndex]) ++ repMaxIndex = i; ++ } ++ if (repLens[repMaxIndex] >= p->numFastBytes) ++ { ++ UInt32 lenRes; ++ *backRes = repMaxIndex; ++ lenRes = repLens[repMaxIndex]; ++ MovePos(p, lenRes - 1); ++ return lenRes; ++ } ++ ++ matches = p->matches; ++ if (mainLen >= p->numFastBytes) ++ { ++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 1); ++ return mainLen; ++ } ++ curByte = *data; ++ matchByte = *(data - (reps[0] + 1)); ++ ++ if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) ++ { ++ *backRes = (UInt32)-1; ++ return 1; ++ } ++ ++ p->opt[0].state = (CState)p->state; ++ ++ posState = (position & p->pbMask); ++ ++ { ++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); ++ p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + ++ (!IsCharState(p->state) ? ++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : ++ LitEnc_GetPrice(probs, curByte, p->ProbPrices)); ++ } ++ ++ MakeAsChar(&p->opt[1]); ++ ++ matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); ++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); ++ ++ if (matchByte == curByte) ++ { ++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); ++ if (shortRepPrice < p->opt[1].price) ++ { ++ p->opt[1].price = shortRepPrice; ++ MakeAsShortRep(&p->opt[1]); ++ } ++ } ++ lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); ++ ++ if (lenEnd < 2) ++ { ++ *backRes = p->opt[1].backPrev; ++ return 1; ++ } ++ ++ p->opt[1].posPrev = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ p->opt[0].backs[i] = reps[i]; ++ ++ len = lenEnd; ++ do ++ p->opt[len--].price = kInfinityPrice; ++ while (len >= 2); ++ ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 repLen = repLens[i]; ++ UInt32 price; ++ if (repLen < 2) ++ continue; ++ price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); ++ do ++ { ++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; ++ COptimal *opt = &p->opt[repLen]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = 0; ++ opt->backPrev = i; ++ opt->prev1IsChar = False; ++ } ++ } ++ while (--repLen >= 2); ++ } ++ ++ normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); ++ ++ len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); ++ if (len <= mainLen) ++ { ++ UInt32 offs = 0; ++ while (len > matches[offs]) ++ offs += 2; ++ for (; ; len++) ++ { ++ COptimal *opt; ++ UInt32 distance = matches[offs + 1]; ++ ++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; ++ UInt32 lenToPosState = GetLenToPosState(len); ++ if (distance < kNumFullDistances) ++ curAndLenPrice += p->distancesPrices[lenToPosState][distance]; ++ else ++ { ++ UInt32 slot; ++ GetPosSlot2(distance, slot); ++ curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; ++ } ++ opt = &p->opt[len]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = 0; ++ opt->backPrev = distance + LZMA_NUM_REPS; ++ opt->prev1IsChar = False; ++ } ++ if (len == matches[offs]) ++ { ++ offs += 2; ++ if (offs == numPairs) ++ break; ++ } ++ } ++ } ++ ++ cur = 0; ++ ++ #ifdef SHOW_STAT2 ++ if (position >= 0) ++ { ++ unsigned i; ++ printf("\n pos = %4X", position); ++ for (i = cur; i <= lenEnd; i++) ++ printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); ++ } ++ #endif ++ ++ for (;;) ++ { ++ UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; ++ UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; ++ Bool nextIsChar; ++ Byte curByte, matchByte; ++ const Byte *data; ++ COptimal *curOpt; ++ COptimal *nextOpt; ++ ++ cur++; ++ if (cur == lenEnd) ++ return Backward(p, backRes, cur); ++ ++ newLen = ReadMatchDistances(p, &numPairs); ++ if (newLen >= p->numFastBytes) ++ { ++ p->numPairs = numPairs; ++ p->longestMatchLength = newLen; ++ return Backward(p, backRes, cur); ++ } ++ position++; ++ curOpt = &p->opt[cur]; ++ posPrev = curOpt->posPrev; ++ if (curOpt->prev1IsChar) ++ { ++ posPrev--; ++ if (curOpt->prev2) ++ { ++ state = p->opt[curOpt->posPrev2].state; ++ if (curOpt->backPrev2 < LZMA_NUM_REPS) ++ state = kRepNextStates[state]; ++ else ++ state = kMatchNextStates[state]; ++ } ++ else ++ state = p->opt[posPrev].state; ++ state = kLiteralNextStates[state]; ++ } ++ else ++ state = p->opt[posPrev].state; ++ if (posPrev == cur - 1) ++ { ++ if (IsShortRep(curOpt)) ++ state = kShortRepNextStates[state]; ++ else ++ state = kLiteralNextStates[state]; ++ } ++ else ++ { ++ UInt32 pos; ++ const COptimal *prevOpt; ++ if (curOpt->prev1IsChar && curOpt->prev2) ++ { ++ posPrev = curOpt->posPrev2; ++ pos = curOpt->backPrev2; ++ state = kRepNextStates[state]; ++ } ++ else ++ { ++ pos = curOpt->backPrev; ++ if (pos < LZMA_NUM_REPS) ++ state = kRepNextStates[state]; ++ else ++ state = kMatchNextStates[state]; ++ } ++ prevOpt = &p->opt[posPrev]; ++ if (pos < LZMA_NUM_REPS) ++ { ++ UInt32 i; ++ reps[0] = prevOpt->backs[pos]; ++ for (i = 1; i <= pos; i++) ++ reps[i] = prevOpt->backs[i - 1]; ++ for (; i < LZMA_NUM_REPS; i++) ++ reps[i] = prevOpt->backs[i]; ++ } ++ else ++ { ++ UInt32 i; ++ reps[0] = (pos - LZMA_NUM_REPS); ++ for (i = 1; i < LZMA_NUM_REPS; i++) ++ reps[i] = prevOpt->backs[i - 1]; ++ } ++ } ++ curOpt->state = (CState)state; ++ ++ curOpt->backs[0] = reps[0]; ++ curOpt->backs[1] = reps[1]; ++ curOpt->backs[2] = reps[2]; ++ curOpt->backs[3] = reps[3]; ++ ++ curPrice = curOpt->price; ++ nextIsChar = False; ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ curByte = *data; ++ matchByte = *(data - (reps[0] + 1)); ++ ++ posState = (position & p->pbMask); ++ ++ curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); ++ { ++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); ++ curAnd1Price += ++ (!IsCharState(state) ? ++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : ++ LitEnc_GetPrice(probs, curByte, p->ProbPrices)); ++ } ++ ++ nextOpt = &p->opt[cur + 1]; ++ ++ if (curAnd1Price < nextOpt->price) ++ { ++ nextOpt->price = curAnd1Price; ++ nextOpt->posPrev = cur; ++ MakeAsChar(nextOpt); ++ nextIsChar = True; ++ } ++ ++ matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); ++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); ++ ++ if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) ++ { ++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); ++ if (shortRepPrice <= nextOpt->price) ++ { ++ nextOpt->price = shortRepPrice; ++ nextOpt->posPrev = cur; ++ MakeAsShortRep(nextOpt); ++ nextIsChar = True; ++ } ++ } ++ numAvailFull = p->numAvail; ++ { ++ UInt32 temp = kNumOpts - 1 - cur; ++ if (temp < numAvailFull) ++ numAvailFull = temp; ++ } ++ ++ if (numAvailFull < 2) ++ continue; ++ numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); ++ ++ if (!nextIsChar && matchByte != curByte) /* speed optimization */ ++ { ++ /* try Literal + rep0 */ ++ UInt32 temp; ++ UInt32 lenTest2; ++ const Byte *data2 = data - (reps[0] + 1); ++ UInt32 limit = p->numFastBytes + 1; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ ++ for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); ++ lenTest2 = temp - 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kLiteralNextStates[state]; ++ UInt32 posStateNext = (position + 1) & p->pbMask; ++ UInt32 nextRepMatchPrice = curAnd1Price + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ UInt32 offset = cur + 1 + lenTest2; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = False; ++ } ++ } ++ } ++ } ++ ++ startLen = 2; /* speed optimization */ ++ { ++ UInt32 repIndex; ++ for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) ++ { ++ UInt32 lenTest; ++ UInt32 lenTestTemp; ++ UInt32 price; ++ const Byte *data2 = data - (reps[repIndex] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); ++ while (lenEnd < cur + lenTest) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ lenTestTemp = lenTest; ++ price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); ++ do ++ { ++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; ++ COptimal *opt = &p->opt[cur + lenTest]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur; ++ opt->backPrev = repIndex; ++ opt->prev1IsChar = False; ++ } ++ } ++ while (--lenTest >= 2); ++ lenTest = lenTestTemp; ++ ++ if (repIndex == 0) ++ startLen = lenTest + 1; ++ ++ /* if (_maxMode) */ ++ { ++ UInt32 lenTest2 = lenTest + 1; ++ UInt32 limit = lenTest2 + p->numFastBytes; ++ UInt32 nextRepMatchPrice; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); ++ lenTest2 -= lenTest + 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kRepNextStates[state]; ++ UInt32 posStateNext = (position + lenTest) & p->pbMask; ++ UInt32 curAndLenCharPrice = ++ price + p->repLenEnc.prices[posState][lenTest - 2] + ++ GET_PRICE_0(p->isMatch[state2][posStateNext]) + ++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), ++ data[lenTest], data2[lenTest], p->ProbPrices); ++ state2 = kLiteralNextStates[state2]; ++ posStateNext = (position + lenTest + 1) & p->pbMask; ++ nextRepMatchPrice = curAndLenCharPrice + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ UInt32 offset = cur + lenTest + 1 + lenTest2; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + lenTest + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = True; ++ opt->posPrev2 = cur; ++ opt->backPrev2 = repIndex; ++ } ++ } ++ } ++ } ++ } ++ } ++ /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ ++ if (newLen > numAvail) ++ { ++ newLen = numAvail; ++ for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); ++ matches[numPairs] = newLen; ++ numPairs += 2; ++ } ++ if (newLen >= startLen) ++ { ++ UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); ++ UInt32 offs, curBack, posSlot; ++ UInt32 lenTest; ++ while (lenEnd < cur + newLen) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ ++ offs = 0; ++ while (startLen > matches[offs]) ++ offs += 2; ++ curBack = matches[offs + 1]; ++ GetPosSlot2(curBack, posSlot); ++ for (lenTest = /*2*/ startLen; ; lenTest++) ++ { ++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; ++ UInt32 lenToPosState = GetLenToPosState(lenTest); ++ COptimal *opt; ++ if (curBack < kNumFullDistances) ++ curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; ++ else ++ curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; ++ ++ opt = &p->opt[cur + lenTest]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur; ++ opt->backPrev = curBack + LZMA_NUM_REPS; ++ opt->prev1IsChar = False; ++ } ++ ++ if (/*_maxMode && */lenTest == matches[offs]) ++ { ++ /* Try Match + Literal + Rep0 */ ++ const Byte *data2 = data - (curBack + 1); ++ UInt32 lenTest2 = lenTest + 1; ++ UInt32 limit = lenTest2 + p->numFastBytes; ++ UInt32 nextRepMatchPrice; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); ++ lenTest2 -= lenTest + 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kMatchNextStates[state]; ++ UInt32 posStateNext = (position + lenTest) & p->pbMask; ++ UInt32 curAndLenCharPrice = curAndLenPrice + ++ GET_PRICE_0(p->isMatch[state2][posStateNext]) + ++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), ++ data[lenTest], data2[lenTest], p->ProbPrices); ++ state2 = kLiteralNextStates[state2]; ++ posStateNext = (posStateNext + 1) & p->pbMask; ++ nextRepMatchPrice = curAndLenCharPrice + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 offset = cur + lenTest + 1 + lenTest2; ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + lenTest + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = True; ++ opt->posPrev2 = cur; ++ opt->backPrev2 = curBack + LZMA_NUM_REPS; ++ } ++ } ++ } ++ offs += 2; ++ if (offs == numPairs) ++ break; ++ curBack = matches[offs + 1]; ++ if (curBack >= kNumFullDistances) ++ GetPosSlot2(curBack, posSlot); ++ } ++ } ++ } ++ } ++} ++ ++#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) ++ ++static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) ++{ ++ UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; ++ const Byte *data; ++ const UInt32 *matches; ++ ++ if (p->additionalOffset == 0) ++ mainLen = ReadMatchDistances(p, &numPairs); ++ else ++ { ++ mainLen = p->longestMatchLength; ++ numPairs = p->numPairs; ++ } ++ ++ numAvail = p->numAvail; ++ *backRes = (UInt32)-1; ++ if (numAvail < 2) ++ return 1; ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ ++ repLen = repIndex = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 len; ++ const Byte *data2 = data - (p->reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ for (len = 2; len < numAvail && data[len] == data2[len]; len++); ++ if (len >= p->numFastBytes) ++ { ++ *backRes = i; ++ MovePos(p, len - 1); ++ return len; ++ } ++ if (len > repLen) ++ { ++ repIndex = i; ++ repLen = len; ++ } ++ } ++ ++ matches = p->matches; ++ if (mainLen >= p->numFastBytes) ++ { ++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 1); ++ return mainLen; ++ } ++ ++ mainDist = 0; /* for GCC */ ++ if (mainLen >= 2) ++ { ++ mainDist = matches[numPairs - 1]; ++ while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) ++ { ++ if (!ChangePair(matches[numPairs - 3], mainDist)) ++ break; ++ numPairs -= 2; ++ mainLen = matches[numPairs - 2]; ++ mainDist = matches[numPairs - 1]; ++ } ++ if (mainLen == 2 && mainDist >= 0x80) ++ mainLen = 1; ++ } ++ ++ if (repLen >= 2 && ( ++ (repLen + 1 >= mainLen) || ++ (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || ++ (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) ++ { ++ *backRes = repIndex; ++ MovePos(p, repLen - 1); ++ return repLen; ++ } ++ ++ if (mainLen < 2 || numAvail <= 2) ++ return 1; ++ ++ p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); ++ if (p->longestMatchLength >= 2) ++ { ++ UInt32 newDistance = matches[p->numPairs - 1]; ++ if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || ++ (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || ++ (p->longestMatchLength > mainLen + 1) || ++ (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) ++ return 1; ++ } ++ ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 len, limit; ++ const Byte *data2 = data - (p->reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ limit = mainLen - 1; ++ for (len = 2; len < limit && data[len] == data2[len]; len++); ++ if (len >= limit) ++ return 1; ++ } ++ *backRes = mainDist + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 2); ++ return mainLen; ++} ++ ++static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) ++{ ++ UInt32 len; ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); ++ p->state = kMatchNextStates[p->state]; ++ len = LZMA_MATCH_LEN_MIN; ++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); ++ RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); ++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); ++} ++ ++static SRes CheckErrors(CLzmaEnc *p) ++{ ++ if (p->result != SZ_OK) ++ return p->result; ++ if (p->rc.res != SZ_OK) ++ p->result = SZ_ERROR_WRITE; ++ if (p->matchFinderBase.result != SZ_OK) ++ p->result = SZ_ERROR_READ; ++ if (p->result != SZ_OK) ++ p->finished = True; ++ return p->result; ++} ++ ++static SRes Flush(CLzmaEnc *p, UInt32 nowPos) ++{ ++ /* ReleaseMFStream(); */ ++ p->finished = True; ++ if (p->writeEndMark) ++ WriteEndMarker(p, nowPos & p->pbMask); ++ RangeEnc_FlushData(&p->rc); ++ RangeEnc_FlushStream(&p->rc); ++ return CheckErrors(p); ++} ++ ++static void FillAlignPrices(CLzmaEnc *p) ++{ ++ UInt32 i; ++ for (i = 0; i < kAlignTableSize; i++) ++ p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); ++ p->alignPriceCount = 0; ++} ++ ++static void FillDistancesPrices(CLzmaEnc *p) ++{ ++ UInt32 tempPrices[kNumFullDistances]; ++ UInt32 i, lenToPosState; ++ for (i = kStartPosModelIndex; i < kNumFullDistances; i++) ++ { ++ UInt32 posSlot = GetPosSlot1(i); ++ UInt32 footerBits = ((posSlot >> 1) - 1); ++ UInt32 base = ((2 | (posSlot & 1)) << footerBits); ++ tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); ++ } ++ ++ for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) ++ { ++ UInt32 posSlot; ++ const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; ++ UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; ++ for (posSlot = 0; posSlot < p->distTableSize; posSlot++) ++ posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); ++ for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) ++ posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); ++ ++ { ++ UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; ++ UInt32 i; ++ for (i = 0; i < kStartPosModelIndex; i++) ++ distancesPrices[i] = posSlotPrices[i]; ++ for (; i < kNumFullDistances; i++) ++ distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; ++ } ++ } ++ p->matchPriceCount = 0; ++} ++ ++static void LzmaEnc_Construct(CLzmaEnc *p) ++{ ++ RangeEnc_Construct(&p->rc); ++ MatchFinder_Construct(&p->matchFinderBase); ++ #ifndef _7ZIP_ST ++ MatchFinderMt_Construct(&p->matchFinderMt); ++ p->matchFinderMt.MatchFinder = &p->matchFinderBase; ++ #endif ++ ++ { ++ CLzmaEncProps props; ++ LzmaEncProps_Init(&props); ++ LzmaEnc_SetProps(p, &props); ++ } ++ ++ #ifndef LZMA_LOG_BSR ++ LzmaEnc_FastPosInit(p->g_FastPos); ++ #endif ++ ++ LzmaEnc_InitPriceTables(p->ProbPrices); ++ p->litProbs = 0; ++ p->saveState.litProbs = 0; ++} ++ ++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) ++{ ++ void *p; ++ p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); ++ if (p != 0) ++ LzmaEnc_Construct((CLzmaEnc *)p); ++ return p; ++} ++ ++static void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->litProbs); ++ alloc->Free(alloc, p->saveState.litProbs); ++ p->litProbs = 0; ++ p->saveState.litProbs = 0; ++} ++ ++static void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ #ifndef _7ZIP_ST ++ MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); ++ #endif ++ MatchFinder_Free(&p->matchFinderBase, allocBig); ++ LzmaEnc_FreeLits(p, alloc); ++ RangeEnc_Free(&p->rc, alloc); ++} ++ ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); ++ alloc->Free(alloc, p); ++} ++ ++static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) ++{ ++ UInt32 nowPos32, startPos32; ++ if (p->needInit) ++ { ++ p->matchFinder.Init(p->matchFinderObj); ++ p->needInit = 0; ++ } ++ ++ if (p->finished) ++ return p->result; ++ RINOK(CheckErrors(p)); ++ ++ nowPos32 = (UInt32)p->nowPos64; ++ startPos32 = nowPos32; ++ ++ if (p->nowPos64 == 0) ++ { ++ UInt32 numPairs; ++ Byte curByte; ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) ++ return Flush(p, nowPos32); ++ ReadMatchDistances(p, &numPairs); ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); ++ p->state = kLiteralNextStates[p->state]; ++ curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); ++ LitEnc_Encode(&p->rc, p->litProbs, curByte); ++ p->additionalOffset--; ++ nowPos32++; ++ } ++ ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) ++ for (;;) ++ { ++ UInt32 pos, len, posState; ++ ++ if (p->fastMode) ++ len = GetOptimumFast(p, &pos); ++ else ++ len = GetOptimum(p, nowPos32, &pos); ++ ++ #ifdef SHOW_STAT2 ++ printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); ++ #endif ++ ++ posState = nowPos32 & p->pbMask; ++ if (len == 1 && pos == (UInt32)-1) ++ { ++ Byte curByte; ++ CLzmaProb *probs; ++ const Byte *data; ++ ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; ++ curByte = *data; ++ probs = LIT_PROBS(nowPos32, *(data - 1)); ++ if (IsCharState(p->state)) ++ LitEnc_Encode(&p->rc, probs, curByte); ++ else ++ LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); ++ p->state = kLiteralNextStates[p->state]; ++ } ++ else ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); ++ if (pos < LZMA_NUM_REPS) ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); ++ if (pos == 0) ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); ++ RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); ++ } ++ else ++ { ++ UInt32 distance = p->reps[pos]; ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); ++ if (pos == 1) ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); ++ else ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); ++ if (pos == 3) ++ p->reps[3] = p->reps[2]; ++ p->reps[2] = p->reps[1]; ++ } ++ p->reps[1] = p->reps[0]; ++ p->reps[0] = distance; ++ } ++ if (len == 1) ++ p->state = kShortRepNextStates[p->state]; ++ else ++ { ++ LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ p->state = kRepNextStates[p->state]; ++ } ++ } ++ else ++ { ++ UInt32 posSlot; ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); ++ p->state = kMatchNextStates[p->state]; ++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ pos -= LZMA_NUM_REPS; ++ GetPosSlot(pos, posSlot); ++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); ++ ++ if (posSlot >= kStartPosModelIndex) ++ { ++ UInt32 footerBits = ((posSlot >> 1) - 1); ++ UInt32 base = ((2 | (posSlot & 1)) << footerBits); ++ UInt32 posReduced = pos - base; ++ ++ if (posSlot < kEndPosModelIndex) ++ RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); ++ else ++ { ++ RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); ++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); ++ p->alignPriceCount++; ++ } ++ } ++ p->reps[3] = p->reps[2]; ++ p->reps[2] = p->reps[1]; ++ p->reps[1] = p->reps[0]; ++ p->reps[0] = pos; ++ p->matchPriceCount++; ++ } ++ } ++ p->additionalOffset -= len; ++ nowPos32 += len; ++ if (p->additionalOffset == 0) ++ { ++ UInt32 processed; ++ if (!p->fastMode) ++ { ++ if (p->matchPriceCount >= (1 << 7)) ++ FillDistancesPrices(p); ++ if (p->alignPriceCount >= kAlignTableSize) ++ FillAlignPrices(p); ++ } ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) ++ break; ++ processed = nowPos32 - startPos32; ++ if (useLimits) ++ { ++ if (processed + kNumOpts + 300 >= maxUnpackSize || ++ RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) ++ break; ++ } ++ else if (processed >= (1 << 15)) ++ { ++ p->nowPos64 += nowPos32 - startPos32; ++ return CheckErrors(p); ++ } ++ } ++ } ++ p->nowPos64 += nowPos32 - startPos32; ++ return Flush(p, nowPos32); ++} ++ ++#define kBigHashDicLimit ((UInt32)1 << 24) ++ ++static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ UInt32 beforeSize = kNumOpts; ++ Bool btMode; ++ if (!RangeEnc_Alloc(&p->rc, alloc)) ++ return SZ_ERROR_MEM; ++ btMode = (p->matchFinderBase.btMode != 0); ++ #ifndef _7ZIP_ST ++ p->mtMode = (p->multiThread && !p->fastMode && btMode); ++ #endif ++ ++ { ++ unsigned lclp = p->lc + p->lp; ++ if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) ++ { ++ LzmaEnc_FreeLits(p, alloc); ++ p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); ++ p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); ++ if (p->litProbs == 0 || p->saveState.litProbs == 0) ++ { ++ LzmaEnc_FreeLits(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ p->lclp = lclp; ++ } ++ } ++ ++ p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); ++ ++ if (beforeSize + p->dictSize < keepWindowSize) ++ beforeSize = keepWindowSize - p->dictSize; ++ ++ #ifndef _7ZIP_ST ++ if (p->mtMode) ++ { ++ RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); ++ p->matchFinderObj = &p->matchFinderMt; ++ MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); ++ } ++ else ++ #endif ++ { ++ if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) ++ return SZ_ERROR_MEM; ++ p->matchFinderObj = &p->matchFinderBase; ++ MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); ++ } ++ return SZ_OK; ++} ++ ++static void LzmaEnc_Init(CLzmaEnc *p) ++{ ++ UInt32 i; ++ p->state = 0; ++ for (i = 0 ; i < LZMA_NUM_REPS; i++) ++ p->reps[i] = 0; ++ ++ RangeEnc_Init(&p->rc); ++ ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ UInt32 j; ++ for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) ++ { ++ p->isMatch[i][j] = kProbInitValue; ++ p->isRep0Long[i][j] = kProbInitValue; ++ } ++ p->isRep[i] = kProbInitValue; ++ p->isRepG0[i] = kProbInitValue; ++ p->isRepG1[i] = kProbInitValue; ++ p->isRepG2[i] = kProbInitValue; ++ } ++ ++ { ++ UInt32 num = 0x300 << (p->lp + p->lc); ++ for (i = 0; i < num; i++) ++ p->litProbs[i] = kProbInitValue; ++ } ++ ++ { ++ for (i = 0; i < kNumLenToPosStates; i++) ++ { ++ CLzmaProb *probs = p->posSlotEncoder[i]; ++ UInt32 j; ++ for (j = 0; j < (1 << kNumPosSlotBits); j++) ++ probs[j] = kProbInitValue; ++ } ++ } ++ { ++ for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) ++ p->posEncoders[i] = kProbInitValue; ++ } ++ ++ LenEnc_Init(&p->lenEnc.p); ++ LenEnc_Init(&p->repLenEnc.p); ++ ++ for (i = 0; i < (1 << kNumAlignBits); i++) ++ p->posAlignEncoder[i] = kProbInitValue; ++ ++ p->optimumEndIndex = 0; ++ p->optimumCurrentIndex = 0; ++ p->additionalOffset = 0; ++ ++ p->pbMask = (1 << p->pb) - 1; ++ p->lpMask = (1 << p->lp) - 1; ++} ++ ++static void LzmaEnc_InitPrices(CLzmaEnc *p) ++{ ++ if (!p->fastMode) ++ { ++ FillDistancesPrices(p); ++ FillAlignPrices(p); ++ } ++ ++ p->lenEnc.tableSize = ++ p->repLenEnc.tableSize = ++ p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; ++ LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); ++ LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); ++} ++ ++static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ UInt32 i; ++ for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) ++ if (p->dictSize <= ((UInt32)1 << i)) ++ break; ++ p->distTableSize = i * 2; ++ ++ p->finished = False; ++ p->result = SZ_OK; ++ RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); ++ LzmaEnc_Init(p); ++ LzmaEnc_InitPrices(p); ++ p->nowPos64 = 0; ++ return SZ_OK; ++} ++ ++static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ p->matchFinderBase.stream = inStream; ++ p->needInit = 1; ++ p->rc.outStream = outStream; ++ return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); ++} ++ ++/*SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ++ ISeqInStream *inStream, UInt32 keepWindowSize, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ p->matchFinderBase.stream = inStream; ++ p->needInit = 1; ++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); ++}*/ ++ ++static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) ++{ ++ p->matchFinderBase.directInput = 1; ++ p->matchFinderBase.bufferBase = (Byte *)src; ++ p->matchFinderBase.directInputRem = srcLen; ++} ++ ++static SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, ++ UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ LzmaEnc_SetInputBuf(p, src, srcLen); ++ p->needInit = 1; ++ ++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); ++} ++ ++static void LzmaEnc_Finish(CLzmaEncHandle pp) ++{ ++ #ifndef _7ZIP_ST ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ if (p->mtMode) ++ MatchFinderMt_ReleaseStream(&p->matchFinderMt); ++ #else ++ pp = pp; ++ #endif ++} ++ ++typedef struct ++{ ++ ISeqOutStream funcTable; ++ Byte *data; ++ SizeT rem; ++ Bool overflow; ++} CSeqOutStreamBuf; ++ ++static size_t MyWrite(void *pp, const void *data, size_t size) ++{ ++ CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; ++ if (p->rem < size) ++ { ++ size = p->rem; ++ p->overflow = True; ++ } ++ memcpy(p->data, data, size); ++ p->rem -= size; ++ p->data += size; ++ return size; ++} ++ ++ ++/*UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) ++{ ++ const CLzmaEnc *p = (CLzmaEnc *)pp; ++ return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); ++}*/ ++ ++/*const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) ++{ ++ const CLzmaEnc *p = (CLzmaEnc *)pp; ++ return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; ++}*/ ++ ++/* SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, ++ Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ UInt64 nowPos64; ++ SRes res; ++ CSeqOutStreamBuf outStream; ++ ++ outStream.funcTable.Write = MyWrite; ++ outStream.data = dest; ++ outStream.rem = *destLen; ++ outStream.overflow = False; ++ ++ p->writeEndMark = False; ++ p->finished = False; ++ p->result = SZ_OK; ++ ++ if (reInit) ++ LzmaEnc_Init(p); ++ LzmaEnc_InitPrices(p); ++ nowPos64 = p->nowPos64; ++ RangeEnc_Init(&p->rc); ++ p->rc.outStream = &outStream.funcTable; ++ ++ res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); ++ ++ *unpackSize = (UInt32)(p->nowPos64 - nowPos64); ++ *destLen -= outStream.rem; ++ if (outStream.overflow) ++ return SZ_ERROR_OUTPUT_EOF; ++ ++ return res; ++}*/ ++ ++static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) ++{ ++ SRes res = SZ_OK; ++ ++ #ifndef _7ZIP_ST ++ Byte allocaDummy[0x300]; ++ int i = 0; ++ for (i = 0; i < 16; i++) ++ allocaDummy[i] = (Byte)i; ++ #endif ++ ++ for (;;) ++ { ++ res = LzmaEnc_CodeOneBlock(p, False, 0, 0); ++ if (res != SZ_OK || p->finished != 0) ++ break; ++ if (progress != 0) ++ { ++ res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); ++ if (res != SZ_OK) ++ { ++ res = SZ_ERROR_PROGRESS; ++ break; ++ } ++ } ++ } ++ LzmaEnc_Finish(p); ++ return res; ++} ++ ++SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); ++ return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); ++} ++ ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ int i; ++ UInt32 dictSize = p->dictSize; ++ if (*size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_PARAM; ++ *size = LZMA_PROPS_SIZE; ++ props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); ++ ++ for (i = 11; i <= 30; i++) ++ { ++ if (dictSize <= ((UInt32)2 << i)) ++ { ++ dictSize = (2 << i); ++ break; ++ } ++ if (dictSize <= ((UInt32)3 << i)) ++ { ++ dictSize = (3 << i); ++ break; ++ } ++ } ++ ++ for (i = 0; i < 4; i++) ++ props[1 + i] = (Byte)(dictSize >> (8 * i)); ++ return SZ_OK; ++} ++ ++SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ SRes res; ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ ++ CSeqOutStreamBuf outStream; ++ ++ LzmaEnc_SetInputBuf(p, src, srcLen); ++ ++ outStream.funcTable.Write = MyWrite; ++ outStream.data = dest; ++ outStream.rem = *destLen; ++ outStream.overflow = False; ++ ++ p->writeEndMark = writeEndMark; ++ ++ p->rc.outStream = &outStream.funcTable; ++ res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); ++ if (res == SZ_OK) ++ res = LzmaEnc_Encode2(p, progress); ++ ++ *destLen -= outStream.rem; ++ if (outStream.overflow) ++ return SZ_ERROR_OUTPUT_EOF; ++ return res; ++} ++ ++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); ++ SRes res; ++ if (p == 0) ++ return SZ_ERROR_MEM; ++ ++ res = LzmaEnc_SetProps(p, props); ++ if (res == SZ_OK) ++ { ++ res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); ++ if (res == SZ_OK) ++ res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, ++ writeEndMark, progress, alloc, allocBig); ++ } ++ ++ LzmaEnc_Destroy(p, alloc, allocBig); ++ return res; ++} +--- /dev/null ++++ b/lib/lzma/Makefile +@@ -0,0 +1,7 @@ ++lzma_compress-objs := LzFind.o LzmaEnc.o ++lzma_decompress-objs := LzmaDec.o ++ ++obj-$(CONFIG_LZMA_COMPRESS) += lzma_compress.o ++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma_decompress.o ++ ++EXTRA_CFLAGS += -Iinclude/linux -Iinclude/linux/lzma -include types.h diff --git a/target/linux/generic/pending-6.12/532-jffs2_eofdetect.patch b/target/linux/generic/pending-6.12/532-jffs2_eofdetect.patch new file mode 100644 index 00000000000..88bb14a02a6 --- /dev/null +++ b/target/linux/generic/pending-6.12/532-jffs2_eofdetect.patch @@ -0,0 +1,65 @@ +From: Felix Fietkau +Subject: fs: jffs2: EOF marker + +Signed-off-by: Felix Fietkau +--- + fs/jffs2/build.c | 10 ++++++++++ + fs/jffs2/scan.c | 21 +++++++++++++++++++-- + 2 files changed, 29 insertions(+), 2 deletions(-) + +--- a/fs/jffs2/build.c ++++ b/fs/jffs2/build.c +@@ -117,6 +117,16 @@ static int jffs2_build_filesystem(struct + dbg_fsbuild("scanned flash completely\n"); + jffs2_dbg_dump_block_lists_nolock(c); + ++ if (c->flags & (1 << 7)) { ++ printk("%s(): unlocking the mtd device... ", __func__); ++ mtd_unlock(c->mtd, 0, c->mtd->size); ++ printk("done.\n"); ++ ++ printk("%s(): erasing all blocks after the end marker... ", __func__); ++ jffs2_erase_pending_blocks(c, -1); ++ printk("done.\n"); ++ } ++ + dbg_fsbuild("pass 1 starting\n"); + c->flags |= JFFS2_SB_FLAG_BUILDING; + /* Now scan the directory tree, increasing nlink according to every dirent found. */ +--- a/fs/jffs2/scan.c ++++ b/fs/jffs2/scan.c +@@ -148,8 +148,14 @@ int jffs2_scan_medium(struct jffs2_sb_in + /* reset summary info for next eraseblock scan */ + jffs2_sum_reset_collected(s); + +- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), +- buf_size, s); ++ if (c->flags & (1 << 7)) { ++ if (mtd_block_isbad(c->mtd, jeb->offset)) ++ ret = BLK_STATE_BADBLOCK; ++ else ++ ret = BLK_STATE_ALLFF; ++ } else ++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), ++ buf_size, s); + + if (ret < 0) + goto out; +@@ -569,6 +575,17 @@ full_scan: + return err; + } + ++ if ((buf[0] == 0xde) && ++ (buf[1] == 0xad) && ++ (buf[2] == 0xc0) && ++ (buf[3] == 0xde)) { ++ /* end of filesystem. erase everything after this point */ ++ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); ++ c->flags |= (1 << 7); ++ ++ return BLK_STATE_ALLFF; ++ } ++ + /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ + ofs = 0; + max_ofs = EMPTY_SCAN_SIZE(c->sector_size); diff --git a/target/linux/generic/pending-6.12/600-netfilter_conntrack_flush.patch b/target/linux/generic/pending-6.12/600-netfilter_conntrack_flush.patch new file mode 100644 index 00000000000..52e97e46efd --- /dev/null +++ b/target/linux/generic/pending-6.12/600-netfilter_conntrack_flush.patch @@ -0,0 +1,90 @@ +From: Felix Fietkau +Subject: netfilter: add support for flushing conntrack via /proc + +lede-commit 8193bbe59a74d34d6a26d4a8cb857b1952905314 +Signed-off-by: Felix Fietkau +--- + net/netfilter/nf_conntrack_standalone.c | 59 ++++++++++++++++++++++++++++++++- + 1 file changed, 58 insertions(+), 1 deletion(-) + +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #ifdef CONFIG_SYSCTL + #include +@@ -458,6 +459,58 @@ static int ct_cpu_seq_show(struct seq_fi + return 0; + } + ++struct kill_request { ++ u16 family; ++ union nf_inet_addr addr; ++}; ++ ++static int kill_matching(struct nf_conn *i, void *data) ++{ ++ struct kill_request *kr = data; ++ struct nf_conntrack_tuple *t1 = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple; ++ struct nf_conntrack_tuple *t2 = &i->tuplehash[IP_CT_DIR_REPLY].tuple; ++ ++ if (!kr->family) ++ return 1; ++ ++ if (t1->src.l3num != kr->family) ++ return 0; ++ ++ return (nf_inet_addr_cmp(&kr->addr, &t1->src.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t1->dst.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t2->src.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t2->dst.u3)); ++} ++ ++static int ct_file_write(struct file *file, char *buf, size_t count) ++{ ++ struct seq_file *seq = file->private_data; ++ struct nf_ct_iter_data iter_data; ++ struct kill_request kr = { }; ++ ++ if (count == 0) ++ return 0; ++ ++ if (count >= INET6_ADDRSTRLEN) ++ count = INET6_ADDRSTRLEN - 1; ++ ++ if (strnchr(buf, count, ':')) { ++ kr.family = AF_INET6; ++ if (!in6_pton(buf, count, (void *)&kr.addr, '\n', NULL)) ++ return -EINVAL; ++ } else if (strnchr(buf, count, '.')) { ++ kr.family = AF_INET; ++ if (!in4_pton(buf, count, (void *)&kr.addr, '\n', NULL)) ++ return -EINVAL; ++ } ++ ++ iter_data.net = seq_file_net(seq); ++ iter_data.data = &kr; ++ nf_ct_iterate_cleanup_net(kill_matching, &iter_data); ++ ++ return 0; ++} ++ + static const struct seq_operations ct_cpu_seq_ops = { + .start = ct_cpu_seq_start, + .next = ct_cpu_seq_next, +@@ -471,8 +524,9 @@ static int nf_conntrack_standalone_init_ + kuid_t root_uid; + kgid_t root_gid; + +- pde = proc_create_net("nf_conntrack", 0440, net->proc_net, &ct_seq_ops, +- sizeof(struct ct_iter_state)); ++ pde = proc_create_net_data_write("nf_conntrack", 0440, net->proc_net, ++ &ct_seq_ops, &ct_file_write, ++ sizeof(struct ct_iter_state), NULL); + if (!pde) + goto out_nf_conntrack; + diff --git a/target/linux/generic/pending-6.12/610-netfilter_match_bypass_default_checks.patch b/target/linux/generic/pending-6.12/610-netfilter_match_bypass_default_checks.patch new file mode 100644 index 00000000000..fd22200a848 --- /dev/null +++ b/target/linux/generic/pending-6.12/610-netfilter_match_bypass_default_checks.patch @@ -0,0 +1,110 @@ +From: Felix Fietkau +Subject: kernel: add a new version of my netfilter speedup patches for linux 2.6.39 and 3.0 + +Signed-off-by: Felix Fietkau +--- + include/uapi/linux/netfilter_ipv4/ip_tables.h | 1 + + net/ipv4/netfilter/ip_tables.c | 37 +++++++++++++++++++++++++++ + 2 files changed, 38 insertions(+) + +--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h ++++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h +@@ -89,6 +89,7 @@ struct ipt_ip { + #define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ + #define IPT_F_GOTO 0x02 /* Set if jump is a goto */ + #define IPT_F_MASK 0x03 /* All possible flag bits mask. */ ++#define IPT_F_NO_DEF_MATCH 0x80 /* Internal: no default match rules present */ + + /* Values for "inv" field in struct ipt_ip. */ + #define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -48,6 +48,9 @@ ip_packet_match(const struct iphdr *ip, + { + unsigned long ret; + ++ if (ipinfo->flags & IPT_F_NO_DEF_MATCH) ++ return true; ++ + if (NF_INVF(ipinfo, IPT_INV_SRCIP, + (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) || + NF_INVF(ipinfo, IPT_INV_DSTIP, +@@ -78,6 +81,29 @@ ip_packet_match(const struct iphdr *ip, + return true; + } + ++static void ++ip_checkdefault(struct ipt_ip *ip) ++{ ++ static const char iface_mask[IFNAMSIZ] = {}; ++ ++ if (ip->invflags || ip->flags & IPT_F_FRAG) ++ return; ++ ++ if (memcmp(ip->iniface_mask, iface_mask, IFNAMSIZ) != 0) ++ return; ++ ++ if (memcmp(ip->outiface_mask, iface_mask, IFNAMSIZ) != 0) ++ return; ++ ++ if (ip->smsk.s_addr || ip->dmsk.s_addr) ++ return; ++ ++ if (ip->proto) ++ return; ++ ++ ip->flags |= IPT_F_NO_DEF_MATCH; ++} ++ + static bool + ip_checkentry(const struct ipt_ip *ip) + { +@@ -523,6 +549,8 @@ find_check_entry(struct ipt_entry *e, st + struct xt_mtchk_param mtpar; + struct xt_entry_match *ematch; + ++ ip_checkdefault(&e->ip); ++ + if (!xt_percpu_counter_alloc(alloc_state, &e->counters)) + return -ENOMEM; + +@@ -817,6 +845,7 @@ copy_entries_to_user(unsigned int total_ + const struct xt_table_info *private = table->private; + int ret = 0; + const void *loc_cpu_entry; ++ u8 flags; + + counters = alloc_counters(table); + if (IS_ERR(counters)) +@@ -844,6 +873,14 @@ copy_entries_to_user(unsigned int total_ + goto free_counters; + } + ++ flags = e->ip.flags & IPT_F_MASK; ++ if (copy_to_user(userptr + off ++ + offsetof(struct ipt_entry, ip.flags), ++ &flags, sizeof(flags)) != 0) { ++ ret = -EFAULT; ++ goto free_counters; ++ } ++ + for (i = sizeof(struct ipt_entry); + i < e->target_offset; + i += m->u.match_size) { +@@ -1225,12 +1262,15 @@ compat_copy_entry_to_user(struct ipt_ent + compat_uint_t origsize; + const struct xt_entry_match *ematch; + int ret = 0; ++ u8 flags = e->ip.flags & IPT_F_MASK; + + origsize = *size; + ce = *dstptr; + if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 || + copy_to_user(&ce->counters, &counters[i], +- sizeof(counters[i])) != 0) ++ sizeof(counters[i])) != 0 || ++ copy_to_user(&ce->ip.flags, &flags, ++ sizeof(flags)) != 0) + return -EFAULT; + + *dstptr += sizeof(struct compat_ipt_entry); diff --git a/target/linux/generic/pending-6.12/611-netfilter_match_bypass_default_table.patch b/target/linux/generic/pending-6.12/611-netfilter_match_bypass_default_table.patch new file mode 100644 index 00000000000..9f0efe4ec41 --- /dev/null +++ b/target/linux/generic/pending-6.12/611-netfilter_match_bypass_default_table.patch @@ -0,0 +1,106 @@ +From: Felix Fietkau +Subject: netfilter: match bypass default table + +Signed-off-by: Felix Fietkau +--- + net/ipv4/netfilter/ip_tables.c | 79 +++++++++++++++++++++++++++++++----------- + 1 file changed, 58 insertions(+), 21 deletions(-) + +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -244,6 +244,33 @@ struct ipt_entry *ipt_next_entry(const s + return (void *)entry + entry->next_offset; + } + ++static bool ++ipt_handle_default_rule(struct ipt_entry *e, unsigned int *verdict) ++{ ++ struct xt_entry_target *t; ++ struct xt_standard_target *st; ++ ++ if (e->target_offset != sizeof(struct ipt_entry)) ++ return false; ++ ++ if (!(e->ip.flags & IPT_F_NO_DEF_MATCH)) ++ return false; ++ ++ t = ipt_get_target(e); ++ if (t->u.kernel.target->target) ++ return false; ++ ++ st = (struct xt_standard_target *) t; ++ if (st->verdict == XT_RETURN) ++ return false; ++ ++ if (st->verdict >= 0) ++ return false; ++ ++ *verdict = (unsigned)(-st->verdict) - 1; ++ return true; ++} ++ + /* Returns one of the generic firewall policies, like NF_ACCEPT. */ + unsigned int + ipt_do_table(void *priv, +@@ -265,27 +292,28 @@ ipt_do_table(void *priv, + unsigned int addend; + + /* Initialization */ ++ WARN_ON(!(table->valid_hooks & (1 << hook))); ++ local_bh_disable(); ++ private = READ_ONCE(table->private); /* Address dependency. */ ++ cpu = smp_processor_id(); ++ table_base = private->entries; ++ ++ e = get_entry(table_base, private->hook_entry[hook]); ++ if (ipt_handle_default_rule(e, &verdict)) { ++ struct xt_counters *counter; ++ ++ counter = xt_get_this_cpu_counter(&e->counters); ++ ADD_COUNTER(*counter, skb->len, 1); ++ local_bh_enable(); ++ return verdict; ++ } ++ + stackidx = 0; + ip = ip_hdr(skb); + indev = state->in ? state->in->name : nulldevname; + outdev = state->out ? state->out->name : nulldevname; +- /* We handle fragments by dealing with the first fragment as +- * if it was a normal packet. All other fragments are treated +- * normally, except that they will NEVER match rules that ask +- * things we don't know, ie. tcp syn flag or ports). If the +- * rule is also a fragment-specific rule, non-fragments won't +- * match it. */ +- acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; +- acpar.thoff = ip_hdrlen(skb); +- acpar.hotdrop = false; +- acpar.state = state; + +- WARN_ON(!(table->valid_hooks & (1 << hook))); +- local_bh_disable(); + addend = xt_write_recseq_begin(); +- private = READ_ONCE(table->private); /* Address dependency. */ +- cpu = smp_processor_id(); +- table_base = private->entries; + jumpstack = (struct ipt_entry **)private->jumpstack[cpu]; + + /* Switch to alternate jumpstack if we're being invoked via TEE. +@@ -298,7 +326,16 @@ ipt_do_table(void *priv, + if (static_key_false(&xt_tee_enabled)) + jumpstack += private->stacksize * __this_cpu_read(nf_skb_duplicated); + +- e = get_entry(table_base, private->hook_entry[hook]); ++ /* We handle fragments by dealing with the first fragment as ++ * if it was a normal packet. All other fragments are treated ++ * normally, except that they will NEVER match rules that ask ++ * things we don't know, ie. tcp syn flag or ports). If the ++ * rule is also a fragment-specific rule, non-fragments won't ++ * match it. */ ++ acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; ++ acpar.thoff = ip_hdrlen(skb); ++ acpar.hotdrop = false; ++ acpar.state = state; + + do { + const struct xt_entry_target *t; diff --git a/target/linux/generic/pending-6.12/612-netfilter_match_reduce_memory_access.patch b/target/linux/generic/pending-6.12/612-netfilter_match_reduce_memory_access.patch new file mode 100644 index 00000000000..7f291fc0083 --- /dev/null +++ b/target/linux/generic/pending-6.12/612-netfilter_match_reduce_memory_access.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +Subject: netfilter: reduce match memory access + +Signed-off-by: Felix Fietkau +--- + net/ipv4/netfilter/ip_tables.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -51,9 +51,9 @@ ip_packet_match(const struct iphdr *ip, + if (ipinfo->flags & IPT_F_NO_DEF_MATCH) + return true; + +- if (NF_INVF(ipinfo, IPT_INV_SRCIP, ++ if (NF_INVF(ipinfo, IPT_INV_SRCIP, ipinfo->smsk.s_addr && + (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) || +- NF_INVF(ipinfo, IPT_INV_DSTIP, ++ NF_INVF(ipinfo, IPT_INV_DSTIP, ipinfo->dmsk.s_addr && + (ip->daddr & ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr)) + return false; + diff --git a/target/linux/generic/pending-6.12/620-net-sfp-improve-Huawei-MA5671a-fixup.patch b/target/linux/generic/pending-6.12/620-net-sfp-improve-Huawei-MA5671a-fixup.patch new file mode 100644 index 00000000000..dd0a4a57f52 --- /dev/null +++ b/target/linux/generic/pending-6.12/620-net-sfp-improve-Huawei-MA5671a-fixup.patch @@ -0,0 +1,149 @@ +From patchwork Fri Mar 6 12:29:55 2026 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= + +X-Patchwork-Id: 14457090 +X-Patchwork-Delegate: kuba@kernel.org +Received: from mail-wr1-f48.google.com (mail-wr1-f48.google.com + [209.85.221.48]) + (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) + (No client certificate requested) + by smtp.subspace.kernel.org (Postfix) with ESMTPS id AF3F4386459 + for ; Fri, 6 Mar 2026 12:52:18 +0000 (UTC) +Authentication-Results: smtp.subspace.kernel.org; + arc=none smtp.client-ip=209.85.221.48 +ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; + t=1772801540; cv=none; + b=LVeywxv8ajenPZ8Kr1arieKosbrf60O9l+ouIPKPFNt5btxWDZ59pIU9BfZjv5n9ifEOyUA/UD0phxnG77+oB/k6UCd7DdGQQASZB3NHq5cvmErbgXm0XG3C8BBxVXU5pF7atPS23kBqM9ptxsv3IaeH/fDFcj6k6SH61rGEpuQ= +ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; + s=arc-20240116; t=1772801540; c=relaxed/simple; + bh=HAy43ssDo0xlUcBDIU7vQZtNnpxG03JPCL6Ldi51ASI=; + h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type; + b=OBk8kI0I91psFRaIxb6nCnAzQlsc7jrXkOPW8lL7cYCosY08yfQDwAlWBFfdFs/VDuVJjD5VEdeQeMt2K4kWGgjLNXhTrRqgs6JNe7PxALDJKvt+kcJ833TRz3hKl2eb2Ft6WnKPf/6hp5Q3qm8+/Q703ixD4sF/0aDNw1BrDY4= +ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; + dmarc=pass (p=none dis=none) header.from=gmail.com; + spf=pass smtp.mailfrom=gmail.com; + dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com + header.b=RCEse1HL; arc=none smtp.client-ip=209.85.221.48 +Authentication-Results: smtp.subspace.kernel.org; + dmarc=pass (p=none dis=none) header.from=gmail.com +Authentication-Results: smtp.subspace.kernel.org; + spf=pass smtp.mailfrom=gmail.com +Authentication-Results: smtp.subspace.kernel.org; + dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com + header.b="RCEse1HL" +Received: by mail-wr1-f48.google.com with SMTP id + ffacd0b85a97d-439b7c2788dso4008389f8f.1 + for ; Fri, 06 Mar 2026 04:52:18 -0800 (PST) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20230601; t=1772801537; x=1773406337; + darn=vger.kernel.org; + h=content-transfer-encoding:mime-version:message-id:date:subject:cc + :to:from:from:to:cc:subject:date:message-id:reply-to; + bh=y8B8kg8ACcCsMXy3SgsyRYngVEpIsqkcoCsLOS/nNqQ=; + b=RCEse1HLoUtQApOdbPXFvYItGrEKWhMZ5FH1L4npAxteGeWOhAEAekijg3Ur83ovNu + D7j0Aio5nwazNQz3y4rO88a+svlEbLx5fyxypjkMFUV4PDnOpv7HYjT9Aw1NVdIwO6l+ + sTgZ1jssfWdVnLQwQe6naotyBRoBV2AugdTmASE0Okxrsi3juIOafyTCxnp4K0weRpaH + XodiSWNrkHzZSWM6/wl3D42yExGGPiuDybF+9otR/5TaBWNzrcLkSb73hvP6va35kQWK + mnp6OV+L7iHTbxYpTfTm4axD+IZ/Q/dtFxxA6XolA28oMQbRPK0SIHepheSZx4bgl64w + FM4w== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20230601; t=1772801537; x=1773406337; + h=content-transfer-encoding:mime-version:message-id:date:subject:cc + :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date + :message-id:reply-to; + bh=y8B8kg8ACcCsMXy3SgsyRYngVEpIsqkcoCsLOS/nNqQ=; + b=KomubXrbvHQI4WbFxBztyfrvNNRRWm7V46yQSwx0bP8PXKIJP38kAYzK+ZKWhmcd7e + LpS7422VcYyLywLRxlevD2YaXsF0CK6e00YpTtixakHxYs/4KxGaU21vfwYV8mRhfu7g + HVmxKvNQ6DTdC7wAIGT6TrcZCK4VCvgCx3z9yC62hQc8C6w+9mDnnGPvXNR74ofvvXdC + eVZjm56layRoEr4PTpR2F33OVSt8+HRikH7eBzIKtQ5n/lEKtmJKDHRaodAaCyFGWMWa + qDVoOR8VI4NIJABfsOT6OqisXLPLf+jkKpGkCY2ioRPRKK9GzW4PgIuNcKvPQilQQkgD + Xlnw== +X-Forwarded-Encrypted: i=1; + AJvYcCVcziiSg1n0cDakmiQXH3869FECP24dcIqrZzs8zKakP+vHT958hnq9Bp0alDnLeVtXgo0B8T4=@vger.kernel.org +X-Gm-Message-State: AOJu0Yx2OF1e3PiuR4Zqpe9qXA6kz6T2CCtro6kv8eL2j4Zh2HCjWywo + /rZTavazOZRoq7zTvc4fGZ/yupjkTT9xRPZCKRkM9pc0UuK/KDSP4pan +X-Gm-Gg: ATEYQzx75s3OlYg8XKMgu042++2+ZPa/CZDw09DYtnwEHHBsuylQF0+eXzcFM166JtP + EMuM6Nq/sGQx2WNTPNEyu1BRGci/SV005CzkExhd1KK52D/nC1c76MBxvAtioaI/+5tgNoyCg8v + ZFRyiqDReKfJ6JHa3YRI213dTzMluN1sZTYNSqlWI1MwW66gaDCf0myU81ehAfiAff34wmxnm8C + PUF0YrLYtgZl1I/ZcYM1npoL3PBOnrhaulSqhbn7S5NaZMkHLrNQm6ns1lof+7Ciju05dQpEcBe + pumVg15Dy+PcSXQSSQt4CULH7bbuJvZ0PHJ7dS+74i/OqFSgxD4E7LCqM5ufHYdbESx0/ERaR/z + CAyT3oTz6S1oMQCUTPevHjHjTbDOWhu74SqyTZETzwGnjZnfrPMa56ebQVRfYgOYW0bbx6j3O2M + v3CSEBiXpdFTdaLuRcqIb56JeDryaHx87SOThqnYP6gMiu7EljKYIhr572Rpgz+UIRkrYyNjL9c + BLmrcmhsXX4hU4X5KocoApkO04w +X-Received: by 2002:a05:600c:8b8b:b0:483:103c:b1ee with SMTP id + 5b1f17b1804b1-48526922599mr34173745e9.8.1772801536778; + Fri, 06 Mar 2026 04:52:16 -0800 (PST) +Received: from skynet.lan + (2a02-9142-4581-3c00-0000-0000-0000-0008.red-2a02-914.customerbaf.ipv6.rima-tde.net. + [2a02:9142:4581:3c00::8]) + by smtp.gmail.com with ESMTPSA id + 5b1f17b1804b1-48527681a3esm76085715e9.4.2026.03.06.04.52.14 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Fri, 06 Mar 2026 04:52:15 -0800 (PST) +From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +To: linux@armlinux.org.uk, + andrew@lunn.ch, + hkallweit1@gmail.com, + davem@davemloft.net, + edumazet@google.com, + kuba@kernel.org, + pabeni@redhat.com, + mnhagan88@gmail.com, + netdev@vger.kernel.org, + linux-kernel@vger.kernel.org +Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +Subject: [PATCH net v3] net: sfp: improve Huawei MA5671a fixup +Date: Fri, 6 Mar 2026 13:29:55 +0100 +Message-ID: <20260306125139.213637-1-noltari@gmail.com> +X-Mailer: git-send-email 2.47.3 +Precedence: bulk +X-Mailing-List: netdev@vger.kernel.org +List-Id: +List-Subscribe: +List-Unsubscribe: +MIME-Version: 1.0 +X-Patchwork-Delegate: kuba@kernel.org + +With the current sfp_fixup_ignore_tx_fault() fixup we ignore the TX_FAULT +signal, but we also need to apply sfp_fixup_ignore_los() in order to be +able to communicate with the module even if the fiber isn't connected for +configuration purposes. +This is needed for all the MA5671a firmwares, excluding the FS modded +firmware. + +Fixes: 2069624dac19 ("net: sfp: Add tx-fault workaround for Huawei MA5671A SFP ONT") +Signed-off-by: Álvaro Fernández Rojas +--- + v3: avoid using a vendor name in the function + v2: rebase on top of net/main instead of linux/master + + drivers/net/phy/sfp.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -360,6 +360,12 @@ static void sfp_fixup_ignore_tx_fault(st + sfp->state_ignore_mask |= SFP_F_TX_FAULT; + } + ++static void sfp_fixup_ignore_tx_fault_and_los(struct sfp *sfp) ++{ ++ sfp_fixup_ignore_tx_fault(sfp); ++ sfp_fixup_ignore_los(sfp); ++} ++ + static void sfp_fixup_ignore_hw(struct sfp *sfp, unsigned int mask) + { + sfp->state_hw_mask &= ~mask; +@@ -523,7 +529,7 @@ static const struct sfp_quirk sfp_quirks + // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd NRZ in + // their EEPROM + SFP_QUIRK("HUAWEI", "MA5671A", sfp_quirk_2500basex, +- sfp_fixup_ignore_tx_fault), ++ sfp_fixup_ignore_tx_fault_and_los), + + // Lantech 8330-262D-E and 8330-265D can operate at 2500base-X, but + // incorrectly report 2500MBd NRZ in their EEPROM. diff --git a/target/linux/generic/pending-6.12/630-packet_socket_type.patch b/target/linux/generic/pending-6.12/630-packet_socket_type.patch new file mode 100644 index 00000000000..769696e404a --- /dev/null +++ b/target/linux/generic/pending-6.12/630-packet_socket_type.patch @@ -0,0 +1,138 @@ +From: Felix Fietkau +Subject: net: add an optimization for dealing with raw sockets + +lede-commit: 4898039703d7315f0f3431c860123338ec3be0f6 +Signed-off-by: Felix Fietkau +--- + include/uapi/linux/if_packet.h | 3 +++ + net/packet/af_packet.c | 34 +++++++++++++++++++++++++++------- + net/packet/internal.h | 1 + + 3 files changed, 31 insertions(+), 7 deletions(-) + +--- a/include/uapi/linux/if_packet.h ++++ b/include/uapi/linux/if_packet.h +@@ -33,6 +33,8 @@ struct sockaddr_ll { + #define PACKET_KERNEL 7 /* To kernel space */ + /* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */ + #define PACKET_FASTROUTE 6 /* Fastrouted frame */ ++#define PACKET_MASK_ANY 0xffffffff /* mask for packet type bits */ ++ + + /* Packet socket options */ + +@@ -60,6 +62,7 @@ struct sockaddr_ll { + #define PACKET_FANOUT_DATA 22 + #define PACKET_IGNORE_OUTGOING 23 + #define PACKET_VNET_HDR_SZ 24 ++#define PACKET_RECV_TYPE 25 + + #define PACKET_FANOUT_HASH 0 + #define PACKET_FANOUT_LB 1 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -1911,6 +1911,7 @@ static int packet_rcv_spkt(struct sk_buf + { + struct sock *sk; + struct sockaddr_pkt *spkt; ++ struct packet_sock *po; + + /* + * When we registered the protocol we saved the socket in the data +@@ -1918,6 +1919,7 @@ static int packet_rcv_spkt(struct sk_buf + */ + + sk = pt->af_packet_priv; ++ po = pkt_sk(sk); + + /* + * Yank back the headers [hope the device set this +@@ -1930,7 +1932,7 @@ static int packet_rcv_spkt(struct sk_buf + * so that this procedure is noop. + */ + +- if (skb->pkt_type == PACKET_LOOPBACK) ++ if (!(po->pkt_type & (1 << skb->pkt_type))) + goto out; + + if (!net_eq(dev_net(dev), sock_net(sk))) +@@ -2175,12 +2177,12 @@ static int packet_rcv(struct sk_buff *sk + int skb_len = skb->len; + unsigned int snaplen, res; + +- if (skb->pkt_type == PACKET_LOOPBACK) +- goto drop; +- + sk = pt->af_packet_priv; + po = pkt_sk(sk); + ++ if (!(po->pkt_type & (1 << skb->pkt_type))) ++ goto drop; ++ + if (!net_eq(dev_net(dev), sock_net(sk))) + goto drop; + +@@ -2304,12 +2306,12 @@ static int tpacket_rcv(struct sk_buff *s + BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h2)) != 32); + BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h3)) != 48); + +- if (skb->pkt_type == PACKET_LOOPBACK) +- goto drop; +- + sk = pt->af_packet_priv; + po = pkt_sk(sk); + ++ if (!(po->pkt_type & (1 << skb->pkt_type))) ++ goto drop; ++ + if (!net_eq(dev_net(dev), sock_net(sk))) + goto drop; + +@@ -3429,6 +3431,7 @@ static int packet_create(struct net *net + mutex_init(&po->pg_vec_lock); + po->rollover = NULL; + po->prot_hook.func = packet_rcv; ++ po->pkt_type = PACKET_MASK_ANY & ~(1 << PACKET_LOOPBACK); + + if (sock->type == SOCK_PACKET) + po->prot_hook.func = packet_rcv_spkt; +@@ -4096,6 +4099,16 @@ packet_setsockopt(struct socket *sock, i + packet_sock_flag_set(po, PACKET_SOCK_QDISC_BYPASS, val); + return 0; + } ++ case PACKET_RECV_TYPE: ++ { ++ unsigned int val; ++ if (optlen != sizeof(val)) ++ return -EINVAL; ++ if (copy_from_sockptr(&val, optval, sizeof(val))) ++ return -EFAULT; ++ po->pkt_type = val & ~BIT(PACKET_LOOPBACK); ++ return 0; ++ } + default: + return -ENOPROTOOPT; + } +@@ -4158,6 +4171,13 @@ static int packet_getsockopt(struct sock + case PACKET_COPY_THRESH: + val = READ_ONCE(pkt_sk(sk)->copy_thresh); + break; ++ case PACKET_RECV_TYPE: ++ if (len > sizeof(unsigned int)) ++ len = sizeof(unsigned int); ++ val = po->pkt_type; ++ ++ data = &val; ++ break; + case PACKET_VERSION: + val = po->tp_version; + break; +--- a/net/packet/internal.h ++++ b/net/packet/internal.h +@@ -131,6 +131,7 @@ struct packet_sock { + struct net_device __rcu *cached_dev; + struct packet_type prot_hook ____cacheline_aligned_in_smp; + atomic_t tp_drops ____cacheline_aligned_in_smp; ++ unsigned int pkt_type; + }; + + #define pkt_sk(ptr) container_of_const(ptr, struct packet_sock, sk) diff --git a/target/linux/generic/pending-6.12/640-net-bridge-fix-switchdev-host-mdb-entry-updates.patch b/target/linux/generic/pending-6.12/640-net-bridge-fix-switchdev-host-mdb-entry-updates.patch new file mode 100644 index 00000000000..8271e062ff0 --- /dev/null +++ b/target/linux/generic/pending-6.12/640-net-bridge-fix-switchdev-host-mdb-entry-updates.patch @@ -0,0 +1,42 @@ +From: Felix Fietkau +Date: Thu, 22 Aug 2024 18:02:17 +0200 +Subject: [PATCH] net: bridge: fix switchdev host mdb entry updates + +When a mdb entry is removed, the bridge switchdev code can issue a +switchdev_port_obj_del call for a port that was not offloaded. + +This leads to an imbalance in switchdev_port_obj_add/del calls, since +br_switchdev_mdb_replay has not been called for the port before. + +This can lead to potential multicast forwarding issues and messages such as: +mt7915e 0000:01:00.0 wl1-ap0: Failed to del Host Multicast Database entry + (object id=3) with error: -ENOENT (-2). + +Fix this issue by checking the port offload status when iterating over +lower devs. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -571,10 +571,18 @@ static void br_switchdev_host_mdb(struct + struct net_bridge_mdb_entry *mp, int type) + { + struct net_device *lower_dev; ++ struct net_bridge_port *port; + struct list_head *iter; + +- netdev_for_each_lower_dev(dev, lower_dev, iter) ++ rcu_read_lock(); ++ netdev_for_each_lower_dev(dev, lower_dev, iter) { ++ port = br_port_get_rcu(lower_dev); ++ if (!port || !port->offload_count) ++ continue; ++ + br_switchdev_host_mdb_one(dev, lower_dev, mp, type); ++ } ++ rcu_read_unlock(); + } + + static int diff --git a/target/linux/generic/pending-6.12/641-net-bridge-switchdev-Don-t-drop-packets-between-port.patch b/target/linux/generic/pending-6.12/641-net-bridge-switchdev-Don-t-drop-packets-between-port.patch new file mode 100644 index 00000000000..531e1c99c65 --- /dev/null +++ b/target/linux/generic/pending-6.12/641-net-bridge-switchdev-Don-t-drop-packets-between-port.patch @@ -0,0 +1,38 @@ +From: "Leon M. Busch-George" +Date: Sun, 20 Oct 2024 18:20:14 +0200 +Subject: [PATCH] net: bridge: switchdev: Don't drop packets between ports with + no hwdom + +nbp_switchdev_allowed_egress uses hwdom to determine whether or not a +packet has already been forwarded to a hardware domain. For +net_bridge_ports that aren't set up to use forward offloading, hwdom is +set to 0. When both ingress and egress port have no hwdom, +'cb->src_hwdom != p->hwdom' indicates that the packet is already known in +the target domain - which it isn't - and the packet is wrongly dropped. + +The error was found on a bridge containing a wifi device and a VLAN +tagging device (e.g. eth0.12). With VLAN filtering, this shouldn't happen. + +This patch adds a check for p->hwdom != 0 before comparing hardware +domains to restore forwarding between ports with hwdom = 0. + +fwd_hwdoms are only set for ports with offloading enabled, which also +implies a valid hwdom, so the check '!test_bit(p->hwdom, &cb->fwd_hwdoms)' +doesn't fail in this way (yet - fingers crossed..) and it is left in place. + +Co-developed-by: Felix Fietkau +Signed-off-by: Felix Fietkau +Signed-off-by: Leon M. Busch-George +--- + +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -70,7 +70,7 @@ bool nbp_switchdev_allowed_egress(const + struct br_input_skb_cb *cb = BR_INPUT_SKB_CB(skb); + + return !test_bit(p->hwdom, &cb->fwd_hwdoms) && +- (!skb->offload_fwd_mark || cb->src_hwdom != p->hwdom); ++ (!skb->offload_fwd_mark || !p->hwdom || cb->src_hwdom != p->hwdom); + } + + /* Flags that can be offloaded to hardware */ diff --git a/target/linux/generic/pending-6.12/650-net-pppoe-implement-GRO-support.patch b/target/linux/generic/pending-6.12/650-net-pppoe-implement-GRO-support.patch new file mode 100644 index 00000000000..2aa55f84b99 --- /dev/null +++ b/target/linux/generic/pending-6.12/650-net-pppoe-implement-GRO-support.patch @@ -0,0 +1,248 @@ +From: Felix Fietkau +Date: Tue, 15 Jul 2025 12:37:45 +0200 +Subject: [PATCH] net: pppoe: implement GRO support + +Only handles packets where the pppoe header length field matches the exact +packet length. Significantly improves rx throughput. + +When running NAT traffic through a MediaTek MT7621 devices from a host +behind PPPoE to a host directly connected via ethernet, the TCP throughput +that the device is able to handle improves from ~130 Mbit/s to ~630 Mbit/s, +using fraglist GRO. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -77,6 +77,7 @@ + #include + #include + #include ++#include + + #include + +@@ -434,7 +435,7 @@ static int pppoe_rcv(struct sk_buff *skb + if (skb->len < len) + goto drop; + +- if (pskb_trim_rcsum(skb, len)) ++ if (!skb_is_gso(skb) && pskb_trim_rcsum(skb, len)) + goto drop; + + ph = pppoe_hdr(skb); +@@ -1176,6 +1177,161 @@ static struct pernet_operations pppoe_ne + .size = sizeof(struct pppoe_net), + }; + ++static u16 ++compare_pppoe_header(struct pppoe_hdr *phdr, struct pppoe_hdr *phdr2) ++{ ++ return (__force __u16)((phdr->sid ^ phdr2->sid) | ++ (phdr->tag[0].tag_type ^ phdr2->tag[0].tag_type)); ++} ++ ++static __be16 pppoe_hdr_proto(struct pppoe_hdr *phdr) ++{ ++ switch (phdr->tag[0].tag_type) { ++ case cpu_to_be16(PPP_IP): ++ return cpu_to_be16(ETH_P_IP); ++ case cpu_to_be16(PPP_IPV6): ++ return cpu_to_be16(ETH_P_IPV6); ++ default: ++ return 0; ++ } ++ ++} ++ ++static struct sk_buff *pppoe_gro_receive(struct list_head *head, ++ struct sk_buff *skb) ++{ ++ const struct packet_offload *ptype; ++ unsigned int hlen, off_pppoe; ++ struct sk_buff *pp = NULL; ++ struct pppoe_hdr *phdr; ++ struct sk_buff *p; ++ int flush = 1; ++ __be16 type; ++ ++ off_pppoe = skb_gro_offset(skb); ++ hlen = off_pppoe + sizeof(*phdr); ++ phdr = skb_gro_header(skb, hlen + 2, off_pppoe); ++ if (unlikely(!phdr)) ++ goto out; ++ ++ /* ignore packets with padding or invalid length */ ++ if (skb_gro_len(skb) != be16_to_cpu(phdr->length) + hlen) ++ goto out; ++ ++ type = pppoe_hdr_proto(phdr); ++ if (!type) ++ goto out; ++ ++ ptype = gro_find_receive_by_type(type); ++ if (!ptype) ++ goto out; ++ ++ flush = 0; ++ ++ list_for_each_entry(p, head, list) { ++ struct pppoe_hdr *phdr2; ++ ++ if (!NAPI_GRO_CB(p)->same_flow) ++ continue; ++ ++ phdr2 = (struct pppoe_hdr *)(p->data + off_pppoe); ++ if (compare_pppoe_header(phdr, phdr2)) ++ NAPI_GRO_CB(p)->same_flow = 0; ++ } ++ ++ skb_gro_pull(skb, sizeof(*phdr) + 2); ++ skb_gro_postpull_rcsum(skb, phdr, sizeof(*phdr) + 2); ++ ++ pp = indirect_call_gro_receive_inet(ptype->callbacks.gro_receive, ++ ipv6_gro_receive, inet_gro_receive, ++ head, skb); ++ ++out: ++ skb_gro_flush_final(skb, pp, flush); ++ ++ return pp; ++} ++ ++static int pppoe_gro_complete(struct sk_buff *skb, int nhoff) ++{ ++ struct pppoe_hdr *phdr = (struct pppoe_hdr *)(skb->data + nhoff); ++ __be16 type = pppoe_hdr_proto(phdr); ++ struct packet_offload *ptype; ++ int len, err; ++ ++ ptype = gro_find_complete_by_type(type); ++ if (!ptype) ++ return -ENOENT; ++ ++ err = INDIRECT_CALL_INET(ptype->callbacks.gro_complete, ++ ipv6_gro_complete, inet_gro_complete, ++ skb, nhoff + sizeof(*phdr) + 2); ++ if (err) ++ return err; ++ ++ len = skb->len - (nhoff + sizeof(*phdr)); ++ phdr->length = cpu_to_be16(len); ++ ++ return 0; ++} ++ ++static struct sk_buff *pppoe_gso_segment(struct sk_buff *skb, ++ netdev_features_t features) ++{ ++ unsigned int pppoe_hlen = sizeof(struct pppoe_hdr) + 2; ++ struct sk_buff *segs = ERR_PTR(-EINVAL); ++ u16 mac_offset = skb->mac_header; ++ struct packet_offload *ptype; ++ u16 mac_len = skb->mac_len; ++ struct pppoe_hdr *phdr; ++ __be16 orig_type, type; ++ int len, nhoff; ++ ++ skb_reset_network_header(skb); ++ nhoff = skb_network_header(skb) - skb_mac_header(skb); ++ ++ if (unlikely(!pskb_may_pull(skb, pppoe_hlen))) ++ goto out; ++ ++ phdr = (struct pppoe_hdr *)skb_network_header(skb); ++ type = pppoe_hdr_proto(phdr); ++ ptype = gro_find_complete_by_type(type); ++ if (!ptype) ++ goto out; ++ ++ orig_type = skb->protocol; ++ __skb_pull(skb, pppoe_hlen); ++ segs = ptype->callbacks.gso_segment(skb, features); ++ if (IS_ERR_OR_NULL(segs)) { ++ skb_gso_error_unwind(skb, orig_type, pppoe_hlen, mac_offset, ++ mac_len); ++ goto out; ++ } ++ ++ skb = segs; ++ do { ++ phdr = (struct pppoe_hdr *)(skb_mac_header(skb) + nhoff); ++ len = skb->len - (nhoff + sizeof(*phdr)); ++ phdr->length = cpu_to_be16(len); ++ skb->network_header = (u8 *)phdr - skb->head; ++ skb->protocol = orig_type; ++ skb_reset_mac_len(skb); ++ } while ((skb = skb->next)); ++ ++out: ++ return segs; ++} ++ ++static struct packet_offload pppoe_packet_offload __read_mostly = { ++ .type = cpu_to_be16(ETH_P_PPP_SES), ++ .priority = 20, ++ .callbacks = { ++ .gro_receive = pppoe_gro_receive, ++ .gro_complete = pppoe_gro_complete, ++ .gso_segment = pppoe_gso_segment, ++ }, ++}; ++ + static int __init pppoe_init(void) + { + int err; +@@ -1192,6 +1348,7 @@ static int __init pppoe_init(void) + if (err) + goto out_unregister_pppoe_proto; + ++ dev_add_offload(&pppoe_packet_offload); + dev_add_pack(&pppoes_ptype); + dev_add_pack(&pppoed_ptype); + register_netdevice_notifier(&pppoe_notifier); +@@ -1211,6 +1368,7 @@ static void __exit pppoe_exit(void) + unregister_netdevice_notifier(&pppoe_notifier); + dev_remove_pack(&pppoed_ptype); + dev_remove_pack(&pppoes_ptype); ++ dev_remove_offload(&pppoe_packet_offload); + unregister_pppox_proto(PX_PROTO_OE); + proto_unregister(&pppoe_sk_proto); + unregister_pernet_device(&pppoe_net_ops); +--- a/net/ipv4/af_inet.c ++++ b/net/ipv4/af_inet.c +@@ -1546,6 +1546,7 @@ out: + + return pp; + } ++EXPORT_INDIRECT_CALLABLE(inet_gro_receive); + + static struct sk_buff *ipip_gro_receive(struct list_head *head, + struct sk_buff *skb) +@@ -1631,6 +1632,7 @@ int inet_gro_complete(struct sk_buff *sk + out: + return err; + } ++EXPORT_INDIRECT_CALLABLE(inet_gro_complete); + + static int ipip_gro_complete(struct sk_buff *skb, int nhoff) + { +--- a/net/ipv6/ip6_offload.c ++++ b/net/ipv6/ip6_offload.c +@@ -306,6 +306,7 @@ out: + + return pp; + } ++EXPORT_INDIRECT_CALLABLE(ipv6_gro_receive); + + static struct sk_buff *sit_ip6ip6_gro_receive(struct list_head *head, + struct sk_buff *skb) +@@ -388,6 +389,7 @@ INDIRECT_CALLABLE_SCOPE int ipv6_gro_com + out: + return err; + } ++EXPORT_INDIRECT_CALLABLE(ipv6_gro_complete); + + static int sit_gro_complete(struct sk_buff *skb, int nhoff) + { diff --git a/target/linux/generic/pending-6.12/655-increase_skb_pad.patch b/target/linux/generic/pending-6.12/655-increase_skb_pad.patch new file mode 100644 index 00000000000..7e2d3dcfbc5 --- /dev/null +++ b/target/linux/generic/pending-6.12/655-increase_skb_pad.patch @@ -0,0 +1,20 @@ +From: Felix Fietkau +Subject: kernel: add a few patches for avoiding unnecessary skb reallocations - significantly improves ethernet<->wireless performance + +lede-commit: 6f89cffc9add6939d44a6b54cf9a5e77849aa7fd +Signed-off-by: Felix Fietkau +--- + include/linux/skbuff.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -3241,7 +3241,7 @@ static inline int pskb_network_may_pull( + * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) + */ + #ifndef NET_SKB_PAD +-#define NET_SKB_PAD max(32, L1_CACHE_BYTES) ++#define NET_SKB_PAD max(64, L1_CACHE_BYTES) + #endif + + int ___pskb_trim(struct sk_buff *skb, unsigned int len); diff --git a/target/linux/generic/pending-6.12/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch b/target/linux/generic/pending-6.12/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch new file mode 100644 index 00000000000..951e7f7e240 --- /dev/null +++ b/target/linux/generic/pending-6.12/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch @@ -0,0 +1,511 @@ +From: Steven Barth +Subject: Add support for MAP-E FMRs (mesh mode) + +MAP-E FMRs (draft-ietf-softwire-map-10) are rules for IPv4-communication +between MAP CEs (mesh mode) without the need to forward such data to a +border relay. This is similar to how 6rd works but for IPv4 over IPv6. + +Signed-off-by: Steven Barth +--- + include/net/ip6_tunnel.h | 13 ++ + include/uapi/linux/if_tunnel.h | 13 ++ + net/ipv6/ip6_tunnel.c | 276 +++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 291 insertions(+), 11 deletions(-) + +--- a/include/net/ip6_tunnel.h ++++ b/include/net/ip6_tunnel.h +@@ -18,6 +18,18 @@ + /* determine capability on a per-packet basis */ + #define IP6_TNL_F_CAP_PER_PACKET 0x40000 + ++/* IPv6 tunnel FMR */ ++struct __ip6_tnl_fmr { ++ struct __ip6_tnl_fmr *next; /* next fmr in list */ ++ struct in6_addr ip6_prefix; ++ struct in_addr ip4_prefix; ++ ++ __u8 ip6_prefix_len; ++ __u8 ip4_prefix_len; ++ __u8 ea_len; ++ __u8 offset; ++}; ++ + struct __ip6_tnl_parm { + char name[IFNAMSIZ]; /* name of tunnel device */ + int link; /* ifindex of underlying L2 interface */ +@@ -29,6 +41,7 @@ struct __ip6_tnl_parm { + __u32 flags; /* tunnel flags */ + struct in6_addr laddr; /* local tunnel end-point address */ + struct in6_addr raddr; /* remote tunnel end-point address */ ++ struct __ip6_tnl_fmr *fmrs; /* FMRs */ + + IP_TUNNEL_DECLARE_FLAGS(i_flags); + IP_TUNNEL_DECLARE_FLAGS(o_flags); +--- a/include/uapi/linux/if_tunnel.h ++++ b/include/uapi/linux/if_tunnel.h +@@ -77,10 +77,23 @@ enum { + IFLA_IPTUN_ENCAP_DPORT, + IFLA_IPTUN_COLLECT_METADATA, + IFLA_IPTUN_FWMARK, ++ IFLA_IPTUN_FMRS, + __IFLA_IPTUN_MAX, + }; + #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) + ++enum { ++ IFLA_IPTUN_FMR_UNSPEC, ++ IFLA_IPTUN_FMR_IP6_PREFIX, ++ IFLA_IPTUN_FMR_IP4_PREFIX, ++ IFLA_IPTUN_FMR_IP6_PREFIX_LEN, ++ IFLA_IPTUN_FMR_IP4_PREFIX_LEN, ++ IFLA_IPTUN_FMR_EA_LEN, ++ IFLA_IPTUN_FMR_OFFSET, ++ __IFLA_IPTUN_FMR_MAX, ++}; ++#define IFLA_IPTUN_FMR_MAX (__IFLA_IPTUN_FMR_MAX - 1) ++ + enum tunnel_encap_types { + TUNNEL_ENCAP_NONE, + TUNNEL_ENCAP_FOU, +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -11,6 +11,9 @@ + * linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c + * + * RFC 2473 ++ * ++ * Changes: ++ * Steven Barth : MAP-E FMR support + */ + + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +@@ -68,9 +71,9 @@ static bool log_ecn_error = true; + module_param(log_ecn_error, bool, 0644); + MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); + +-static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2) ++static u32 HASH(const struct in6_addr *addr) + { +- u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2); ++ u32 hash = ipv6_addr_hash(addr); + + return hash_32(hash, IP6_TUNNEL_HASH_SIZE_SHIFT); + } +@@ -115,17 +118,33 @@ static struct ip6_tnl * + ip6_tnl_lookup(struct net *net, int link, + const struct in6_addr *remote, const struct in6_addr *local) + { +- unsigned int hash = HASH(remote, local); ++ unsigned int hash = HASH(local); + struct ip6_tnl *t, *cand = NULL; + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + struct in6_addr any; + + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(local, &t->parms.laddr) || +- !ipv6_addr_equal(remote, &t->parms.raddr) || + !(t->dev->flags & IFF_UP)) + continue; + ++ if (!ipv6_addr_equal(remote, &t->parms.raddr)) { ++ struct __ip6_tnl_fmr *fmr; ++ bool found = false; ++ ++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) { ++ if (!ipv6_prefix_equal(remote, &fmr->ip6_prefix, ++ fmr->ip6_prefix_len)) ++ continue; ++ ++ found = true; ++ break; ++ } ++ ++ if (!found) ++ continue; ++ } ++ + if (link == t->parms.link) + return t; + else +@@ -133,7 +152,7 @@ ip6_tnl_lookup(struct net *net, int link + } + + memset(&any, 0, sizeof(any)); +- hash = HASH(&any, local); ++ hash = HASH(local); + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(local, &t->parms.laddr) || + !ipv6_addr_any(&t->parms.raddr) || +@@ -146,7 +165,7 @@ ip6_tnl_lookup(struct net *net, int link + cand = t; + } + +- hash = HASH(remote, &any); ++ hash = HASH(&any); + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(remote, &t->parms.raddr) || + !ipv6_addr_any(&t->parms.laddr) || +@@ -195,7 +214,7 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, + + if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { + prio = 1; +- h = HASH(remote, local); ++ h = HASH(local); + } + return &ip6n->tnls[prio][h]; + } +@@ -376,6 +395,12 @@ ip6_tnl_dev_uninit(struct net_device *de + struct net *net = t->net; + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + ++ while (t->parms.fmrs) { ++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next; ++ kfree(t->parms.fmrs); ++ t->parms.fmrs = next; ++ } ++ + if (dev == ip6n->fb_tnl_dev) + RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL); + else +@@ -790,6 +815,107 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t, + } + EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl); + ++/** ++ * ip4ip6_fmr_calc - calculate target / source IPv6-address based on FMR ++ * @dest: destination IPv6 address buffer ++ * @skb: received socket buffer ++ * @fmr: MAP FMR ++ * @xmit: Calculate for xmit or rcv ++ **/ ++static void ip4ip6_fmr_calc(struct in6_addr *dest, ++ const struct iphdr *iph, const uint8_t *end, ++ const struct __ip6_tnl_fmr *fmr, bool xmit) ++{ ++ int psidlen = fmr->ea_len - (32 - fmr->ip4_prefix_len); ++ u8 *portp = NULL; ++ bool use_dest_addr; ++ const struct iphdr *dsth = iph; ++ ++ if ((u8*)dsth >= end) ++ return; ++ ++ /* find significant IP header */ ++ if (iph->protocol == IPPROTO_ICMP) { ++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4); ++ if (ih && ((u8*)&ih[1]) <= end && ( ++ ih->type == ICMP_DEST_UNREACH || ++ ih->type == ICMP_SOURCE_QUENCH || ++ ih->type == ICMP_TIME_EXCEEDED || ++ ih->type == ICMP_PARAMETERPROB || ++ ih->type == ICMP_REDIRECT)) ++ dsth = (const struct iphdr*)&ih[1]; ++ } ++ ++ /* in xmit-path use dest port by default and source port only if ++ this is an ICMP reply to something else; vice versa in rcv-path */ ++ use_dest_addr = (xmit && dsth == iph) || (!xmit && dsth != iph); ++ ++ /* get dst port */ ++ if (((u8*)&dsth[1]) <= end && ( ++ dsth->protocol == IPPROTO_UDP || ++ dsth->protocol == IPPROTO_TCP || ++ dsth->protocol == IPPROTO_SCTP || ++ dsth->protocol == IPPROTO_DCCP)) { ++ /* for UDP, TCP, SCTP and DCCP source and dest port ++ follow IPv4 header directly */ ++ portp = ((u8*)dsth) + dsth->ihl * 4; ++ ++ if (use_dest_addr) ++ portp += sizeof(u16); ++ } else if (iph->protocol == IPPROTO_ICMP) { ++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4); ++ ++ /* use icmp identifier as port */ ++ if (((u8*)&ih) <= end && ( ++ (use_dest_addr && ( ++ ih->type == ICMP_ECHOREPLY || ++ ih->type == ICMP_TIMESTAMPREPLY || ++ ih->type == ICMP_INFO_REPLY || ++ ih->type == ICMP_ADDRESSREPLY)) || ++ (!use_dest_addr && ( ++ ih->type == ICMP_ECHO || ++ ih->type == ICMP_TIMESTAMP || ++ ih->type == ICMP_INFO_REQUEST || ++ ih->type == ICMP_ADDRESS) ++ ))) ++ portp = (u8*)&ih->un.echo.id; ++ } ++ ++ if ((portp && &portp[2] <= end) || psidlen == 0) { ++ int frombyte = fmr->ip6_prefix_len / 8; ++ int fromrem = fmr->ip6_prefix_len % 8; ++ int bytes = sizeof(struct in6_addr) - frombyte; ++ const u32 *addr = (use_dest_addr) ? &iph->daddr : &iph->saddr; ++ u64 eabits = ((u64)ntohl(*addr)) << (32 + fmr->ip4_prefix_len); ++ u64 t = 0; ++ ++ /* extract PSID from port and add it to eabits */ ++ u16 psidbits = 0; ++ if (psidlen > 0) { ++ psidbits = ((u16)portp[0]) << 8 | ((u16)portp[1]); ++ psidbits >>= 16 - psidlen - fmr->offset; ++ psidbits = (u16)(psidbits << (16 - psidlen)); ++ eabits |= ((u64)psidbits) << (48 - (fmr->ea_len - psidlen)); ++ } ++ ++ /* rewrite destination address */ ++ *dest = fmr->ip6_prefix; ++ memcpy(&dest->s6_addr[10], addr, sizeof(*addr)); ++ dest->s6_addr16[7] = htons(psidbits >> (16 - psidlen)); ++ ++ if (bytes > sizeof(u64)) ++ bytes = sizeof(u64); ++ ++ /* insert eabits */ ++ memcpy(&t, &dest->s6_addr[frombyte], bytes); ++ t = be64_to_cpu(t) & ~(((((u64)1) << fmr->ea_len) - 1) ++ << (64 - fmr->ea_len - fromrem)); ++ t = cpu_to_be64(t | (eabits >> fromrem)); ++ memcpy(&dest->s6_addr[frombyte], &t, bytes); ++ } ++} ++ ++ + static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, + const struct tnl_ptk_info *tpi, + struct metadata_dst *tun_dst, +@@ -855,6 +981,27 @@ static int __ip6_tnl_rcv(struct ip6_tnl + + memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); + ++ if (tpi->proto == htons(ETH_P_IP) && tunnel->parms.fmrs && ++ !ipv6_addr_equal(&ipv6h->saddr, &tunnel->parms.raddr)) { ++ /* Packet didn't come from BR, so lookup FMR */ ++ struct __ip6_tnl_fmr *fmr; ++ struct in6_addr expected = tunnel->parms.raddr; ++ for (fmr = tunnel->parms.fmrs; fmr; fmr = fmr->next) ++ if (ipv6_prefix_equal(&ipv6h->saddr, ++ &fmr->ip6_prefix, fmr->ip6_prefix_len)) ++ break; ++ ++ /* Check that IPv6 matches IPv4 source to prevent spoofing */ ++ if (fmr) ++ ip4ip6_fmr_calc(&expected, ip_hdr(skb), ++ skb_tail_pointer(skb), fmr, false); ++ ++ if (!ipv6_addr_equal(&ipv6h->saddr, &expected)) { ++ rcu_read_unlock(); ++ goto drop; ++ } ++ } ++ + __skb_tunnel_rx(skb, tunnel->dev, tunnel->net); + + err = dscp_ecn_decapsulate(tunnel, ipv6h, skb); +@@ -1004,6 +1151,7 @@ static void init_tel_txopt(struct ipv6_t + opt->ops.opt_nflen = 8; + } + ++ + /** + * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own + * @t: the outgoing tunnel device +@@ -1293,6 +1441,7 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str + u8 protocol) + { + struct ip6_tnl *t = netdev_priv(dev); ++ struct __ip6_tnl_fmr *fmr; + struct ipv6hdr *ipv6h; + const struct iphdr *iph; + int encap_limit = -1; +@@ -1392,6 +1541,18 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + dsfield = INET_ECN_encapsulate(dsfield, orig_dsfield); + ++ /* try to find matching FMR */ ++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) { ++ unsigned mshift = 32 - fmr->ip4_prefix_len; ++ if (ntohl(fmr->ip4_prefix.s_addr) >> mshift == ++ ntohl(ip_hdr(skb)->daddr) >> mshift) ++ break; ++ } ++ ++ /* change dstaddr according to FMR */ ++ if (fmr) ++ ip4ip6_fmr_calc(&fl6.daddr, ip_hdr(skb), skb_tail_pointer(skb), fmr, true); ++ + if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) + return -1; + +@@ -1545,6 +1706,14 @@ ip6_tnl_change(struct ip6_tnl *t, const + t->parms.link = p->link; + t->parms.proto = p->proto; + t->parms.fwmark = p->fwmark; ++ ++ while (t->parms.fmrs) { ++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next; ++ kfree(t->parms.fmrs); ++ t->parms.fmrs = next; ++ } ++ t->parms.fmrs = p->fmrs; ++ + dst_cache_reset(&t->dst_cache); + ip6_tnl_link_config(t); + } +@@ -1579,6 +1748,7 @@ ip6_tnl_parm_from_user(struct __ip6_tnl_ + p->flowinfo = u->flowinfo; + p->link = u->link; + p->proto = u->proto; ++ p->fmrs = NULL; + memcpy(p->name, u->name, sizeof(u->name)); + } + +@@ -1962,6 +2132,15 @@ static int ip6_tnl_validate(struct nlatt + return 0; + } + ++static const struct nla_policy ip6_tnl_fmr_policy[IFLA_IPTUN_FMR_MAX + 1] = { ++ [IFLA_IPTUN_FMR_IP6_PREFIX] = { .len = sizeof(struct in6_addr) }, ++ [IFLA_IPTUN_FMR_IP4_PREFIX] = { .len = sizeof(struct in_addr) }, ++ [IFLA_IPTUN_FMR_IP6_PREFIX_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_IP4_PREFIX_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_EA_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_OFFSET] = { .type = NLA_U8 } ++}; ++ + static void ip6_tnl_netlink_parms(struct nlattr *data[], + struct __ip6_tnl_parm *parms) + { +@@ -1999,6 +2178,46 @@ static void ip6_tnl_netlink_parms(struct + + if (data[IFLA_IPTUN_FWMARK]) + parms->fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]); ++ ++ if (data[IFLA_IPTUN_FMRS]) { ++ unsigned rem; ++ struct nlattr *fmr; ++ nla_for_each_nested(fmr, data[IFLA_IPTUN_FMRS], rem) { ++ struct nlattr *fmrd[IFLA_IPTUN_FMR_MAX + 1], *c; ++ struct __ip6_tnl_fmr *nfmr; ++ ++ nla_parse_nested(fmrd, IFLA_IPTUN_FMR_MAX, ++ fmr, ip6_tnl_fmr_policy, NULL); ++ ++ if (!(nfmr = kzalloc(sizeof(*nfmr), GFP_KERNEL))) ++ continue; ++ ++ nfmr->offset = 6; ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX])) ++ nla_memcpy(&nfmr->ip6_prefix, fmrd[IFLA_IPTUN_FMR_IP6_PREFIX], ++ sizeof(nfmr->ip6_prefix)); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX])) ++ nla_memcpy(&nfmr->ip4_prefix, fmrd[IFLA_IPTUN_FMR_IP4_PREFIX], ++ sizeof(nfmr->ip4_prefix)); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX_LEN])) ++ nfmr->ip6_prefix_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX_LEN])) ++ nfmr->ip4_prefix_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_EA_LEN])) ++ nfmr->ea_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_OFFSET])) ++ nfmr->offset = nla_get_u8(c); ++ ++ nfmr->next = parms->fmrs; ++ parms->fmrs = nfmr; ++ } ++ } + } + + static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev, +@@ -2083,6 +2302,12 @@ static void ip6_tnl_dellink(struct net_d + + static size_t ip6_tnl_get_size(const struct net_device *dev) + { ++ const struct ip6_tnl *t = netdev_priv(dev); ++ struct __ip6_tnl_fmr *c; ++ int fmrs = 0; ++ for (c = t->parms.fmrs; c; c = c->next) ++ ++fmrs; ++ + return + /* IFLA_IPTUN_LINK */ + nla_total_size(4) + +@@ -2112,6 +2337,24 @@ static size_t ip6_tnl_get_size(const str + nla_total_size(0) + + /* IFLA_IPTUN_FWMARK */ + nla_total_size(4) + ++ /* IFLA_IPTUN_FMRS */ ++ nla_total_size(0) + ++ ( ++ /* nest */ ++ nla_total_size(0) + ++ /* IFLA_IPTUN_FMR_IP6_PREFIX */ ++ nla_total_size(sizeof(struct in6_addr)) + ++ /* IFLA_IPTUN_FMR_IP4_PREFIX */ ++ nla_total_size(sizeof(struct in_addr)) + ++ /* IFLA_IPTUN_FMR_EA_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_IP6_PREFIX_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_IP4_PREFIX_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_OFFSET */ ++ nla_total_size(1) ++ ) * fmrs + + 0; + } + +@@ -2119,6 +2362,9 @@ static int ip6_tnl_fill_info(struct sk_b + { + struct ip6_tnl *tunnel = netdev_priv(dev); + struct __ip6_tnl_parm *parm = &tunnel->parms; ++ struct __ip6_tnl_fmr *c; ++ int fmrcnt = 0; ++ struct nlattr *fmrs; + + if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || + nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) || +@@ -2128,9 +2374,27 @@ static int ip6_tnl_fill_info(struct sk_b + nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) || + nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) || + nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto) || +- nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark)) ++ nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark) || ++ !(fmrs = nla_nest_start(skb, IFLA_IPTUN_FMRS))) + goto nla_put_failure; + ++ for (c = parm->fmrs; c; c = c->next) { ++ struct nlattr *fmr = nla_nest_start(skb, ++fmrcnt); ++ if (!fmr || ++ nla_put(skb, IFLA_IPTUN_FMR_IP6_PREFIX, ++ sizeof(c->ip6_prefix), &c->ip6_prefix) || ++ nla_put(skb, IFLA_IPTUN_FMR_IP4_PREFIX, ++ sizeof(c->ip4_prefix), &c->ip4_prefix) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, c->ip6_prefix_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, c->ip4_prefix_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_EA_LEN, c->ea_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_OFFSET, c->offset)) ++ goto nla_put_failure; ++ ++ nla_nest_end(skb, fmr); ++ } ++ nla_nest_end(skb, fmrs); ++ + if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, tunnel->encap.type) || + nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT, tunnel->encap.sport) || + nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) || +@@ -2170,6 +2434,7 @@ static const struct nla_policy ip6_tnl_p + [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 }, + [IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG }, + [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 }, ++ [IFLA_IPTUN_FMRS] = { .type = NLA_NESTED }, + }; + + static struct rtnl_link_ops ip6_link_ops __read_mostly = { diff --git a/target/linux/generic/pending-6.12/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch b/target/linux/generic/pending-6.12/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch new file mode 100644 index 00000000000..7e99fae6b80 --- /dev/null +++ b/target/linux/generic/pending-6.12/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch @@ -0,0 +1,263 @@ +From: Jonas Gorski +Subject: ipv6: allow rejecting with "source address failed policy" + +RFC6204 L-14 requires rejecting traffic from invalid addresses with +ICMPv6 Destination Unreachable, Code 5 (Source address failed ingress/ +egress policy) on the LAN side, so add an appropriate rule for that. + +Signed-off-by: Jonas Gorski +--- + include/net/netns/ipv6.h | 1 + + include/uapi/linux/fib_rules.h | 4 +++ + include/uapi/linux/rtnetlink.h | 1 + + net/ipv4/fib_semantics.c | 4 +++ + net/ipv4/fib_trie.c | 1 + + net/ipv4/ipmr.c | 1 + + net/ipv6/fib6_rules.c | 4 +++ + net/ipv6/ip6mr.c | 2 ++ + net/ipv6/route.c | 58 +++++++++++++++++++++++++++++++++++++++++- + 9 files changed, 75 insertions(+), 1 deletion(-) + +--- a/include/net/netns/ipv6.h ++++ b/include/net/netns/ipv6.h +@@ -86,6 +86,7 @@ struct netns_ipv6 { + unsigned int fib6_routes_require_src; + #endif + struct rt6_info *ip6_prohibit_entry; ++ struct rt6_info *ip6_policy_failed_entry; + struct rt6_info *ip6_blk_hole_entry; + struct fib6_table *fib6_local_tbl; + struct fib_rules_ops *fib6_rules_ops; +--- a/include/uapi/linux/fib_rules.h ++++ b/include/uapi/linux/fib_rules.h +@@ -83,6 +83,10 @@ enum { + FR_ACT_BLACKHOLE, /* Drop without notification */ + FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */ + FR_ACT_PROHIBIT, /* Drop with EACCES */ ++ FR_ACT_RES9, ++ FR_ACT_RES10, ++ FR_ACT_RES11, ++ FR_ACT_POLICY_FAILED, /* Drop with EACCES */ + __FR_ACT_MAX, + }; + +--- a/include/uapi/linux/rtnetlink.h ++++ b/include/uapi/linux/rtnetlink.h +@@ -265,6 +265,7 @@ enum { + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ ++ RTN_POLICY_FAILED, /* Failed ingress/egress policy */ + __RTN_MAX + }; + +--- a/net/ipv4/fib_semantics.c ++++ b/net/ipv4/fib_semantics.c +@@ -145,6 +145,10 @@ const struct fib_prop fib_props[RTN_MAX + .error = -EINVAL, + .scope = RT_SCOPE_NOWHERE, + }, ++ [RTN_POLICY_FAILED] = { ++ .error = -EACCES, ++ .scope = RT_SCOPE_UNIVERSE, ++ }, + }; + + static void rt_fibinfo_free(struct rtable __rcu **rtp) +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -2763,6 +2763,7 @@ static const char *const rtn_type_names[ + [RTN_THROW] = "THROW", + [RTN_NAT] = "NAT", + [RTN_XRESOLVE] = "XRESOLVE", ++ [RTN_POLICY_FAILED] = "POLICY_FAILED", + }; + + static inline const char *rtn_type(char *buf, size_t len, unsigned int t) +--- a/net/ipv4/ipmr.c ++++ b/net/ipv4/ipmr.c +@@ -191,6 +191,7 @@ static int ipmr_rule_action(struct fib_r + case FR_ACT_UNREACHABLE: + return -ENETUNREACH; + case FR_ACT_PROHIBIT: ++ case FR_ACT_POLICY_FAILED: + return -EACCES; + case FR_ACT_BLACKHOLE: + default: +--- a/net/ipv6/fib6_rules.c ++++ b/net/ipv6/fib6_rules.c +@@ -222,6 +222,10 @@ static int __fib6_rule_action(struct fib + err = -EACCES; + rt = net->ipv6.ip6_prohibit_entry; + goto discard_pkt; ++ case FR_ACT_POLICY_FAILED: ++ err = -EACCES; ++ rt = net->ipv6.ip6_policy_failed_entry; ++ goto discard_pkt; + } + + tb_id = fib_rule_get_table(rule, arg); +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -180,6 +180,8 @@ static int ip6mr_rule_action(struct fib_ + return -ENETUNREACH; + case FR_ACT_PROHIBIT: + return -EACCES; ++ case FR_ACT_POLICY_FAILED: ++ return -EACCES; + case FR_ACT_BLACKHOLE: + default: + return -EINVAL; +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -98,6 +98,8 @@ static int ip6_pkt_discard(struct sk_bu + static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); + static int ip6_pkt_prohibit(struct sk_buff *skb); + static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); ++static int ip6_pkt_policy_failed(struct sk_buff *skb); ++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb); + static void ip6_link_failure(struct sk_buff *skb); + static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, + struct sk_buff *skb, u32 mtu, +@@ -316,6 +318,18 @@ static const struct rt6_info ip6_prohibi + .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), + }; + ++static const struct rt6_info ip6_policy_failed_entry_template = { ++ .dst = { ++ .__rcuref = RCUREF_INIT(1), ++ .__use = 1, ++ .obsolete = DST_OBSOLETE_FORCE_CHK, ++ .error = -EACCES, ++ .input = ip6_pkt_policy_failed, ++ .output = ip6_pkt_policy_failed_out, ++ }, ++ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), ++}; ++ + static const struct rt6_info ip6_blk_hole_entry_template = { + .dst = { + .__rcuref = RCUREF_INIT(1), +@@ -1086,6 +1100,7 @@ static const int fib6_prop[RTN_MAX + 1] + [RTN_BLACKHOLE] = -EINVAL, + [RTN_UNREACHABLE] = -EHOSTUNREACH, + [RTN_PROHIBIT] = -EACCES, ++ [RTN_POLICY_FAILED] = -EACCES, + [RTN_THROW] = -EAGAIN, + [RTN_NAT] = -EINVAL, + [RTN_XRESOLVE] = -EINVAL, +@@ -1121,6 +1136,10 @@ static void ip6_rt_init_dst_reject(struc + rt->dst.output = ip6_pkt_prohibit_out; + rt->dst.input = ip6_pkt_prohibit; + break; ++ case RTN_POLICY_FAILED: ++ rt->dst.output = ip6_pkt_policy_failed_out; ++ rt->dst.input = ip6_pkt_policy_failed; ++ break; + case RTN_THROW: + case RTN_UNREACHABLE: + default: +@@ -4609,6 +4628,17 @@ static int ip6_pkt_prohibit_out(struct n + return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); + } + ++static int ip6_pkt_policy_failed(struct sk_buff *skb) ++{ ++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_INNOROUTES); ++} ++ ++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb) ++{ ++ skb->dev = skb_dst(skb)->dev; ++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_OUTNOROUTES); ++} ++ + /* + * Allocate a dst for local (unicast / anycast) address. + */ +@@ -5100,7 +5130,8 @@ static int rtm_to_fib6_config(struct sk_ + if (rtm->rtm_type == RTN_UNREACHABLE || + rtm->rtm_type == RTN_BLACKHOLE || + rtm->rtm_type == RTN_PROHIBIT || +- rtm->rtm_type == RTN_THROW) ++ rtm->rtm_type == RTN_THROW || ++ rtm->rtm_type == RTN_POLICY_FAILED) + cfg->fc_flags |= RTF_REJECT; + + if (rtm->rtm_type == RTN_LOCAL) +@@ -6371,6 +6402,8 @@ static int ip6_route_dev_notify(struct n + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + net->ipv6.ip6_prohibit_entry->dst.dev = dev; + net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); ++ net->ipv6.ip6_policy_failed_entry->dst.dev = dev; ++ net->ipv6.ip6_policy_failed_entry->rt6i_idev = in6_dev_get(dev); + net->ipv6.ip6_blk_hole_entry->dst.dev = dev; + net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); + #endif +@@ -6382,6 +6415,7 @@ static int ip6_route_dev_notify(struct n + in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev); ++ in6_dev_put_clear(&net->ipv6.ip6_policy_failed_entry->rt6i_idev); + in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev); + #endif + } +@@ -6577,6 +6611,8 @@ static int __net_init ip6_route_net_init + + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + net->ipv6.fib6_has_custom_rules = false; ++ ++ + net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, + sizeof(*net->ipv6.ip6_prohibit_entry), + GFP_KERNEL); +@@ -6587,11 +6623,21 @@ static int __net_init ip6_route_net_init + ip6_template_metrics, true); + INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->dst.rt_uncached); + ++ net->ipv6.ip6_policy_failed_entry = ++ kmemdup(&ip6_policy_failed_entry_template, ++ sizeof(*net->ipv6.ip6_policy_failed_entry), GFP_KERNEL); ++ if (!net->ipv6.ip6_policy_failed_entry) ++ goto out_ip6_prohibit_entry; ++ net->ipv6.ip6_policy_failed_entry->dst.ops = &net->ipv6.ip6_dst_ops; ++ dst_init_metrics(&net->ipv6.ip6_policy_failed_entry->dst, ++ ip6_template_metrics, true); ++ INIT_LIST_HEAD(&net->ipv6.ip6_policy_failed_entry->dst.rt_uncached); ++ + net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, + sizeof(*net->ipv6.ip6_blk_hole_entry), + GFP_KERNEL); + if (!net->ipv6.ip6_blk_hole_entry) +- goto out_ip6_prohibit_entry; ++ goto out_ip6_policy_failed_entry; + net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; + dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, + ip6_template_metrics, true); +@@ -6618,6 +6664,8 @@ out: + return ret; + + #ifdef CONFIG_IPV6_MULTIPLE_TABLES ++out_ip6_policy_failed_entry: ++ kfree(net->ipv6.ip6_policy_failed_entry); + out_ip6_prohibit_entry: + kfree(net->ipv6.ip6_prohibit_entry); + out_ip6_null_entry: +@@ -6637,6 +6685,7 @@ static void __net_exit ip6_route_net_exi + kfree(net->ipv6.ip6_null_entry); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + kfree(net->ipv6.ip6_prohibit_entry); ++ kfree(net->ipv6.ip6_policy_failed_entry); + kfree(net->ipv6.ip6_blk_hole_entry); + #endif + dst_entries_destroy(&net->ipv6.ip6_dst_ops); +@@ -6720,6 +6769,9 @@ void __init ip6_route_init_special_entri + init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); + init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; + init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); ++ init_net.ipv6.ip6_policy_failed_entry->dst.dev = init_net.loopback_dev; ++ init_net.ipv6.ip6_policy_failed_entry->rt6i_idev = ++ in6_dev_get(init_net.loopback_dev); + #endif + } + diff --git a/target/linux/generic/pending-6.12/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch b/target/linux/generic/pending-6.12/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch new file mode 100644 index 00000000000..7057d364dbc --- /dev/null +++ b/target/linux/generic/pending-6.12/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch @@ -0,0 +1,50 @@ +From: Jonas Gorski +Subject: net: provide defines for _POLICY_FAILED until all code is updated + +Upstream introduced ICMPV6_POLICY_FAIL for code 5 of destination +unreachable, conflicting with our name. + +Add appropriate defines to allow our code to build with the new +name until we have updated our local patches for older kernels +and userspace packages. + +Signed-off-by: Jonas Gorski +--- + include/uapi/linux/fib_rules.h | 2 ++ + include/uapi/linux/icmpv6.h | 2 ++ + include/uapi/linux/rtnetlink.h | 2 ++ + 3 files changed, 6 insertions(+) + +--- a/include/uapi/linux/fib_rules.h ++++ b/include/uapi/linux/fib_rules.h +@@ -90,6 +90,8 @@ enum { + __FR_ACT_MAX, + }; + ++#define FR_ACT_FAILED_POLICY FR_ACT_POLICY_FAILED ++ + #define FR_ACT_MAX (__FR_ACT_MAX - 1) + + #endif +--- a/include/uapi/linux/icmpv6.h ++++ b/include/uapi/linux/icmpv6.h +@@ -127,6 +127,8 @@ struct icmp6hdr { + #define ICMPV6_POLICY_FAIL 5 + #define ICMPV6_REJECT_ROUTE 6 + ++#define ICMPV6_FAILED_POLICY ICMPV6_POLICY_FAIL ++ + /* + * Codes for Time Exceeded + */ +--- a/include/uapi/linux/rtnetlink.h ++++ b/include/uapi/linux/rtnetlink.h +@@ -269,6 +269,8 @@ enum { + __RTN_MAX + }; + ++#define RTN_FAILED_POLICY RTN_POLICY_FAILED ++ + #define RTN_MAX (__RTN_MAX - 1) + + diff --git a/target/linux/generic/pending-6.12/681-net-remove-NETIF_F_GSO_FRAGLIST-from-NETIF_F_GSO_SOF.patch b/target/linux/generic/pending-6.12/681-net-remove-NETIF_F_GSO_FRAGLIST-from-NETIF_F_GSO_SOF.patch new file mode 100644 index 00000000000..fdfacd8dee9 --- /dev/null +++ b/target/linux/generic/pending-6.12/681-net-remove-NETIF_F_GSO_FRAGLIST-from-NETIF_F_GSO_SOF.patch @@ -0,0 +1,129 @@ +From: Felix Fietkau +Date: Thu, 15 Aug 2024 21:15:13 +0200 +Subject: [PATCH] net: remove NETIF_F_GSO_FRAGLIST from NETIF_F_GSO_SOFTWARE + +Several drivers set NETIF_F_GSO_SOFTWARE, but mangle fraglist GRO packets +in a way that they can't be properly segmented anymore. +In order to properly deal with this, remove fraglist GSO from +NETIF_F_GSO_SOFTWARE and switch to NETIF_F_GSO_SOFTWARE_ALL (which includes +fraglist GSO) in places where it's safe to add. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/dummy.c ++++ b/drivers/net/dummy.c +@@ -111,7 +111,7 @@ static void dummy_setup(struct net_devic + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; + dev->lltx = true; + dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST; +- dev->features |= NETIF_F_GSO_SOFTWARE; ++ dev->features |= NETIF_F_GSO_SOFTWARE_ALL; + dev->features |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA; + dev->features |= NETIF_F_GSO_ENCAP_ALL; + dev->hw_features |= dev->features; +--- a/drivers/net/loopback.c ++++ b/drivers/net/loopback.c +@@ -174,7 +174,7 @@ static void gen_lo_setup(struct net_devi + dev->lltx = true; + dev->netns_local = true; + netif_keep_dst(dev); +- dev->hw_features = NETIF_F_GSO_SOFTWARE; ++ dev->hw_features = NETIF_F_GSO_SOFTWARE_ALL; + dev->features = NETIF_F_SG | NETIF_F_FRAGLIST + | NETIF_F_GSO_SOFTWARE + | NETIF_F_HW_CSUM +--- a/drivers/net/macvlan.c ++++ b/drivers/net/macvlan.c +@@ -903,7 +903,7 @@ static int macvlan_hwtstamp_set(struct n + static struct lock_class_key macvlan_netdev_addr_lock_key; + + #define ALWAYS_ON_OFFLOADS \ +- (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE | \ ++ (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE_ALL | \ + NETIF_F_GSO_ROBUST | NETIF_F_GSO_ENCAP_ALL) + + #define ALWAYS_ON_FEATURES ALWAYS_ON_OFFLOADS +--- a/include/linux/netdev_features.h ++++ b/include/linux/netdev_features.h +@@ -211,13 +211,14 @@ static inline int find_next_netdev_featu + + /* List of features with software fallbacks. */ + #define NETIF_F_GSO_SOFTWARE (NETIF_F_ALL_TSO | NETIF_F_GSO_SCTP | \ +- NETIF_F_GSO_UDP_L4 | NETIF_F_GSO_FRAGLIST) ++ NETIF_F_GSO_UDP_L4) ++#define NETIF_F_GSO_SOFTWARE_ALL (NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_FRAGLIST) + + /* + * If one device supports one of these features, then enable them + * for all in netdev_increment_features. + */ +-#define NETIF_F_ONE_FOR_ALL (NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ROBUST | \ ++#define NETIF_F_ONE_FOR_ALL (NETIF_F_GSO_SOFTWARE_ALL | NETIF_F_GSO_ROBUST | \ + NETIF_F_SG | NETIF_F_HIGHDMA | \ + NETIF_F_FRAGLIST | NETIF_F_VLAN_CHALLENGED) + +--- a/net/8021q/vlan.h ++++ b/net/8021q/vlan.h +@@ -109,7 +109,7 @@ static inline netdev_features_t vlan_tnl + netdev_features_t ret; + + ret = real_dev->hw_enc_features & +- (NETIF_F_CSUM_MASK | NETIF_F_GSO_SOFTWARE | ++ (NETIF_F_CSUM_MASK | NETIF_F_GSO_SOFTWARE_ALL | + NETIF_F_GSO_ENCAP_ALL); + + if ((ret & NETIF_F_GSO_ENCAP_ALL) && (ret & NETIF_F_CSUM_MASK)) +--- a/net/8021q/vlan_dev.c ++++ b/net/8021q/vlan_dev.c +@@ -538,7 +538,7 @@ static int vlan_dev_init(struct net_devi + dev->state |= (1 << __LINK_STATE_NOCARRIER); + + dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | +- NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | ++ NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE_ALL | + NETIF_F_GSO_ENCAP_ALL | + NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC | + NETIF_F_FCOE_CRC | NETIF_F_FSO; +@@ -634,7 +634,7 @@ static netdev_features_t vlan_dev_fix_fe + if (lower_features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)) + lower_features |= NETIF_F_HW_CSUM; + features = netdev_intersect_features(features, lower_features); +- features |= old_features & (NETIF_F_SOFT_FEATURES | NETIF_F_GSO_SOFTWARE); ++ features |= old_features & (NETIF_F_SOFT_FEATURES | NETIF_F_GSO_SOFTWARE_ALL); + + return features; + } +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -2557,7 +2557,7 @@ void sk_setup_caps(struct sock *sk, stru + icsk->icsk_ack.dst_quick_ack = dst_metric(dst, RTAX_QUICKACK); + } + if (sk->sk_route_caps & NETIF_F_GSO) +- sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE; ++ sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE_ALL; + if (unlikely(sk->sk_gso_disabled)) + sk->sk_route_caps &= ~NETIF_F_GSO_MASK; + if (sk_can_gso(sk)) { +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -2023,7 +2023,7 @@ void ieee80211_color_collision_detection + /* interface handling */ + #define MAC80211_SUPPORTED_FEATURES_TX (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \ + NETIF_F_HW_CSUM | NETIF_F_SG | \ +- NETIF_F_HIGHDMA | NETIF_F_GSO_SOFTWARE | \ ++ NETIF_F_HIGHDMA | NETIF_F_GSO_SOFTWARE_ALL | \ + NETIF_F_HW_TC) + #define MAC80211_SUPPORTED_FEATURES_RX (NETIF_F_RXCSUM) + #define MAC80211_SUPPORTED_FEATURES (MAC80211_SUPPORTED_FEATURES_TX | \ +--- a/net/openvswitch/vport-internal_dev.c ++++ b/net/openvswitch/vport-internal_dev.c +@@ -109,7 +109,7 @@ static void do_setup(struct net_device * + netdev->rtnl_link_ops = &internal_dev_link_ops; + + netdev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | +- NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE | ++ NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE_ALL | + NETIF_F_GSO_ENCAP_ALL; + + netdev->vlan_features = netdev->features; diff --git a/target/linux/generic/pending-6.12/683-of_net-add-mac-address-to-of-tree.patch b/target/linux/generic/pending-6.12/683-of_net-add-mac-address-to-of-tree.patch new file mode 100644 index 00000000000..0fb02dbb673 --- /dev/null +++ b/target/linux/generic/pending-6.12/683-of_net-add-mac-address-to-of-tree.patch @@ -0,0 +1,75 @@ +From 8585756342caa6d27008d1ad0c18023e4211a40a Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 12:22:48 +0200 +Subject: [PATCH] of/of_net: write back netdev MAC-address to device-tree + +The label-mac logic relies on the mac-address property of a netdev +devices of-node. However, the mac address can also be stored as a +different property or read from e.g. an mtd device. + +Create this node when reading a mac-address from OF if it does not +already exist and copy the mac-address used for the device to this +property. This way, the MAC address can be accessed using procfs. + +--- + net/core/of_net.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +--- a/net/core/of_net.c ++++ b/net/core/of_net.c +@@ -97,6 +97,27 @@ int of_get_mac_address_nvmem(struct devi + } + EXPORT_SYMBOL(of_get_mac_address_nvmem); + ++static int of_add_mac_address(struct device_node *np, u8* addr) ++{ ++ struct property *prop; ++ ++ prop = kzalloc(sizeof(*prop), GFP_KERNEL); ++ if (!prop) ++ return -ENOMEM; ++ ++ prop->name = "mac-address"; ++ prop->length = ETH_ALEN; ++ prop->value = kmemdup(addr, ETH_ALEN, GFP_KERNEL); ++ if (!prop->value || of_update_property(np, prop)) ++ goto free; ++ ++ return 0; ++free: ++ kfree(prop->value); ++ kfree(prop); ++ return -ENOMEM; ++} ++ + /** + * of_get_mac_address() + * @np: Caller's Device Node +@@ -132,17 +153,23 @@ int of_get_mac_address(struct device_nod + + ret = of_get_mac_addr(np, "mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "local-mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "address", addr); + if (!ret) +- return 0; ++ goto found; + +- return of_get_mac_address_nvmem(np, addr); ++ ret = of_get_mac_address_nvmem(np, addr); ++ if (ret) ++ return ret; ++ ++found: ++ ret = of_add_mac_address(np, addr); ++ return ret; + } + EXPORT_SYMBOL(of_get_mac_address); + diff --git a/target/linux/generic/pending-6.12/690-net-add-missing-check-for-TCP-fraglist-GRO.patch b/target/linux/generic/pending-6.12/690-net-add-missing-check-for-TCP-fraglist-GRO.patch new file mode 100644 index 00000000000..4684e045d2d --- /dev/null +++ b/target/linux/generic/pending-6.12/690-net-add-missing-check-for-TCP-fraglist-GRO.patch @@ -0,0 +1,26 @@ +From 4498f0aa561092bc656bfabe7c4bdae41bc4a5b4 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Tue, 7 May 2024 11:24:50 +0200 +Subject: [PATCH] net: add missing check for TCP fraglist GRO + +It turns out that the existing checks do not guarantee that the skb can be +pulled up to the GRO offset. When using the usb r8152 network driver with +GRO fraglist, the BUG() in __skb_pull is often triggered. +Fix the crash by adding the missing check. + +Fixes: 8d95dc474f85 ("net: add code for TCP fraglist GRO") +Signed-off-by: Felix Fietkau +--- + net/ipv4/tcp_offload.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/net/ipv4/tcp_offload.c ++++ b/net/ipv4/tcp_offload.c +@@ -355,6 +355,7 @@ struct sk_buff *tcp_gro_receive(struct l + flush |= (__force int)(flags ^ tcp_flag_word(th2)); + flush |= skb->ip_summed != p->ip_summed; + flush |= skb->csum_level != p->csum_level; ++ flush |= !pskb_may_pull(skb, skb_gro_offset(skb)); + flush |= NAPI_GRO_CB(p)->count >= 64; + skb_set_network_header(skb, skb_gro_receive_network_offset(skb)); + diff --git a/target/linux/generic/pending-6.12/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch b/target/linux/generic/pending-6.12/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch new file mode 100644 index 00000000000..59868b2a535 --- /dev/null +++ b/target/linux/generic/pending-6.12/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch @@ -0,0 +1,106 @@ +From: Pablo Neira Ayuso +Date: Thu, 25 Jan 2018 12:58:55 +0100 +Subject: [PATCH] netfilter: nft_flow_offload: handle netdevice events from + nf_flow_table + +Move the code that deals with device events to the core. + +Signed-off-by: Pablo Neira Ayuso +--- + +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -658,6 +658,23 @@ static struct pernet_operations nf_flow_ + .exit_batch = nf_flow_table_pernet_exit, + }; + ++static int nf_flow_table_netdev_event(struct notifier_block *this, ++ unsigned long event, void *ptr) ++{ ++ struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ ++ if (event != NETDEV_DOWN) ++ return NOTIFY_DONE; ++ ++ nf_flow_table_cleanup(dev); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block flow_offload_netdev_notifier = { ++ .notifier_call = nf_flow_table_netdev_event, ++}; ++ + static int __init nf_flow_table_module_init(void) + { + int ret; +@@ -674,6 +691,10 @@ static int __init nf_flow_table_module_i + if (ret) + goto out_bpf; + ++ ret = register_netdevice_notifier(&flow_offload_netdev_notifier); ++ if (ret) ++ goto out_bpf; ++ + return 0; + + out_bpf: +@@ -685,6 +706,7 @@ out_offload: + + static void __exit nf_flow_table_module_exit(void) + { ++ unregister_netdevice_notifier(&flow_offload_netdev_notifier); + nf_flow_table_offload_exit(); + unregister_pernet_subsys(&nf_flow_table_net_ops); + } +--- a/net/netfilter/nft_flow_offload.c ++++ b/net/netfilter/nft_flow_offload.c +@@ -494,47 +494,14 @@ static struct nft_expr_type nft_flow_off + .owner = THIS_MODULE, + }; + +-static int flow_offload_netdev_event(struct notifier_block *this, +- unsigned long event, void *ptr) +-{ +- struct net_device *dev = netdev_notifier_info_to_dev(ptr); +- +- if (event != NETDEV_DOWN) +- return NOTIFY_DONE; +- +- nf_flow_table_cleanup(dev); +- +- return NOTIFY_DONE; +-} +- +-static struct notifier_block flow_offload_netdev_notifier = { +- .notifier_call = flow_offload_netdev_event, +-}; +- + static int __init nft_flow_offload_module_init(void) + { +- int err; +- +- err = register_netdevice_notifier(&flow_offload_netdev_notifier); +- if (err) +- goto err; +- +- err = nft_register_expr(&nft_flow_offload_type); +- if (err < 0) +- goto register_expr; +- +- return 0; +- +-register_expr: +- unregister_netdevice_notifier(&flow_offload_netdev_notifier); +-err: +- return err; ++ return nft_register_expr(&nft_flow_offload_type); + } + + static void __exit nft_flow_offload_module_exit(void) + { + nft_unregister_expr(&nft_flow_offload_type); +- unregister_netdevice_notifier(&flow_offload_netdev_notifier); + } + + module_init(nft_flow_offload_module_init); diff --git a/target/linux/generic/pending-6.12/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch b/target/linux/generic/pending-6.12/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch new file mode 100644 index 00000000000..d07b282e25d --- /dev/null +++ b/target/linux/generic/pending-6.12/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau +Date: Thu, 31 Aug 2023 21:48:38 +0200 +Subject: [PATCH] netfilter: nf_tables: ignore -EOPNOTSUPP on flowtable device + offload setup + +On many embedded devices, it is common to configure flowtable offloading for +a mix of different devices, some of which have hardware offload support and +some of which don't. +The current code limits the ability of user space to properly set up such a +configuration by only allowing adding devices with hardware offload support to +a offload-enabled flowtable. +Given that offload-enabled flowtables also imply fallback to pure software +offloading, this limitation makes little sense. +Fix it by not bailing out when the offload setup returns -EOPNOTSUPP + +Signed-off-by: Felix Fietkau +--- + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -8724,7 +8724,7 @@ static int nft_register_flowtable_net_ho + err = flowtable->data.type->setup(&flowtable->data, + hook->ops.dev, + FLOW_BLOCK_BIND); +- if (err < 0) ++ if (err < 0 && err != -EOPNOTSUPP) + goto err_unregister_net_hooks; + + err = nf_register_net_hook(net, &hook->ops); diff --git a/target/linux/generic/pending-6.12/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch b/target/linux/generic/pending-6.12/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch new file mode 100644 index 00000000000..2fdb59d1d84 --- /dev/null +++ b/target/linux/generic/pending-6.12/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch @@ -0,0 +1,21 @@ +From: Felix Fietkau +Date: Mon, 21 Mar 2022 20:39:59 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: enable threaded NAPI + +This can improve performance under load by ensuring that NAPI processing is +not pinned on CPU 0. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -5157,6 +5157,8 @@ static int mtk_probe(struct platform_dev + dev_err(eth->dev, "failed to allocated dummy device\n"); + goto err_unreg_netdev; + } ++ eth->dummy_dev->threaded = 1; ++ strcpy(eth->dummy_dev->name, "mtk_eth"); + netif_napi_add(eth->dummy_dev, ð->tx_napi, mtk_napi_tx); + netif_napi_add(eth->dummy_dev, ð->rx_napi, mtk_napi_rx); + diff --git a/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch new file mode 100644 index 00000000000..4e00118e533 --- /dev/null +++ b/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -0,0 +1,38 @@ +From: Gabor Juhos +Subject: generic: add detach callback to struct phy_driver + +lede-commit: fe61fc2d7d0b3fb348b502f68f98243b3ddf5867 + +Signed-off-by: Gabor Juhos +--- + drivers/net/phy/phy_device.c | 3 +++ + include/linux/phy.h | 6 ++++++ + 2 files changed, 9 insertions(+) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2037,6 +2037,9 @@ void phy_detach(struct phy_device *phyde + phydev->devlink = NULL; + } + ++ if (phydev->drv && phydev->drv->detach) ++ phydev->drv->detach(phydev); ++ + if (phydev->sysfs_links) { + if (dev) + sysfs_remove_link(&dev->dev.kobj, "phydev"); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1037,6 +1037,12 @@ struct phy_driver { + /** @handle_interrupt: Override default interrupt handling */ + irqreturn_t (*handle_interrupt)(struct phy_device *phydev); + ++ /* ++ * Called before an ethernet device is detached ++ * from the PHY. ++ */ ++ void (*detach)(struct phy_device *phydev); ++ + /** @remove: Clears up any memory if needed */ + void (*remove)(struct phy_device *phydev); + diff --git a/target/linux/generic/pending-6.12/704-net-phy-register-phy-led_triggers-during-probe-to-av.patch b/target/linux/generic/pending-6.12/704-net-phy-register-phy-led_triggers-during-probe-to-av.patch new file mode 100644 index 00000000000..126275c7ecb --- /dev/null +++ b/target/linux/generic/pending-6.12/704-net-phy-register-phy-led_triggers-during-probe-to-av.patch @@ -0,0 +1,116 @@ +From 5225349f1e750dfd107a4c5dc97d91fa212dc1ed Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +Date: Sat, 21 Feb 2026 14:51:54 -0600 +Subject: [PATCH] net: phy: register phy led_triggers during probe to avoid + AB-BA deadlock + +There is an AB-BA deadlock when both LEDS_TRIGGER_NETDEV and +LED_TRIGGER_PHY are enabled: + +[ 1362.049207] [<8054e4b8>] led_trigger_register+0x5c/0x1fc <-- Trying to get lock "triggers_list_lock" via down_write(&triggers_list_lock); +[ 1362.054536] [<80662830>] phy_led_triggers_register+0xd0/0x234 +[ 1362.060329] [<8065e200>] phy_attach_direct+0x33c/0x40c +[ 1362.065489] [<80651fc4>] phylink_fwnode_phy_connect+0x15c/0x23c +[ 1362.071480] [<8066ee18>] mtk_open+0x7c/0xba0 +[ 1362.075849] [<806d714c>] __dev_open+0x280/0x2b0 +[ 1362.080384] [<806d7668>] __dev_change_flags+0x244/0x24c +[ 1362.085598] [<806d7698>] dev_change_flags+0x28/0x78 +[ 1362.090528] [<807150e4>] dev_ioctl+0x4c0/0x654 <-- Hold lock "rtnl_mutex" by calling rtnl_lock(); +[ 1362.094985] [<80694360>] sock_ioctl+0x2f4/0x4e0 +[ 1362.099567] [<802e9c4c>] sys_ioctl+0x32c/0xd8c +[ 1362.104022] [<80014504>] syscall_common+0x34/0x58 + +Here LED_TRIGGER_PHY is registering LED triggers during phy_attach +while holding RTNL and then taking triggers_list_lock. + +[ 1362.191101] [<806c2640>] register_netdevice_notifier+0x60/0x168 <-- Trying to get lock "rtnl_mutex" via rtnl_lock(); +[ 1362.197073] [<805504ac>] netdev_trig_activate+0x194/0x1e4 +[ 1362.202490] [<8054e28c>] led_trigger_set+0x1d4/0x360 <-- Hold lock "triggers_list_lock" by down_read(&triggers_list_lock); +[ 1362.207511] [<8054eb38>] led_trigger_write+0xd8/0x14c +[ 1362.212566] [<80381d98>] sysfs_kf_bin_write+0x80/0xbc +[ 1362.217688] [<8037fcd8>] kernfs_fop_write_iter+0x17c/0x28c +[ 1362.223174] [<802cbd70>] vfs_write+0x21c/0x3c4 +[ 1362.227712] [<802cc0c4>] ksys_write+0x78/0x12c +[ 1362.232164] [<80014504>] syscall_common+0x34/0x58 + +Here LEDS_TRIGGER_NETDEV is being enabled on an LED. It first takes +triggers_list_lock and then RTNL. A classical AB-BA deadlock. + +phy_led_triggers_registers() does not require the RTNL, it does not +make any calls into the network stack which require protection. There +is also no requirement the PHY has been attached to a MAC, the +triggers only make use of phydev state. This allows the call to +phy_led_triggers_registers() to be placed elsewhere. PHY probe() and +release() don't hold RTNL, so solving the AB-BA deadlock. + +Reported-by: Shiji Yang +Closes: https://lore.kernel.org/all/OS7PR01MB13602B128BA1AD3FA38B6D1FFBC69A@OS7PR01MB13602.jpnprd01.prod.outlook.com/ +Fixes: 06f502f57d0d ("leds: trigger: Introduce a NETDEV trigger") +Signed-off-by: Andrew Lunn +--- + drivers/net/phy/phy_device.c | 25 +++++++++++++++++-------- + 1 file changed, 17 insertions(+), 8 deletions(-) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1684,8 +1684,6 @@ int phy_attach_direct(struct net_device + goto error; + + phy_resume(phydev); +- if (!phydev->is_on_sfp_module) +- phy_led_triggers_register(phydev); + + /** + * If the external phy used by current mac interface is managed by +@@ -2058,9 +2056,6 @@ void phy_detach(struct phy_device *phyde + } + phydev->phylink = NULL; + +- if (!phydev->is_on_sfp_module) +- phy_led_triggers_unregister(phydev); +- + if (phydev->mdio.dev.driver) + module_put(phydev->mdio.dev.driver->owner); + +@@ -3691,17 +3686,28 @@ static int phy_probe(struct device *dev) + /* Set the state to READY by default */ + phydev->state = PHY_READY; + ++ /* Register the PHY LED triggers */ ++ if (!phydev->is_on_sfp_module) ++ phy_led_triggers_register(phydev); ++ + /* Get the LEDs from the device tree, and instantiate standard + * LEDs for them. + */ + if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev) && +- !phy_driver_is_genphy_10g(phydev)) ++ !phy_driver_is_genphy_10g(phydev)) { + err = of_phy_leds(phydev); ++ if (err) ++ goto out; ++ } ++ ++ return 0; + + out: ++ if (!phydev->is_on_sfp_module) ++ phy_led_triggers_unregister(phydev); ++ + /* Re-assert the reset signal on error */ +- if (err) +- phy_device_reset(phydev, 1); ++ phy_device_reset(phydev, 1); + + return err; + } +@@ -3716,6 +3722,9 @@ static int phy_remove(struct device *dev + !phy_driver_is_genphy_10g(phydev)) + phy_leds_unregister(phydev); + ++ if (!phydev->is_on_sfp_module) ++ phy_led_triggers_unregister(phydev); ++ + phydev->state = PHY_DOWN; + + sfp_bus_del_upstream(phydev->sfp_bus); diff --git a/target/linux/generic/pending-6.12/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch b/target/linux/generic/pending-6.12/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch new file mode 100644 index 00000000000..f7e4e777732 --- /dev/null +++ b/target/linux/generic/pending-6.12/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch @@ -0,0 +1,28 @@ +From: Felix Fietkau +Date: Fri, 6 May 2022 21:38:42 +0200 +Subject: [PATCH] net: dsa: tag_mtk: add padding for tx packets + +Padding for transmitted packets needs to account for the special tag. +With not enough padding, garbage bytes are inserted by the switch at the +end of small packets. + +Fixes: 5cd8985a1909 ("net-next: dsa: add Mediatek tag RX/TX handler") +Signed-off-by: Felix Fietkau +--- + +--- a/net/dsa/tag_mtk.c ++++ b/net/dsa/tag_mtk.c +@@ -29,6 +29,13 @@ static struct sk_buff *mtk_tag_xmit(stru + + skb_set_queue_mapping(skb, dp->index); + ++ /* The Ethernet switch we are interfaced with needs packets to be at ++ * least 64 bytes (including FCS) otherwise their padding might be ++ * corrupted. With tags enabled, we need to make sure that packets are ++ * at least 68 bytes (including FCS and tag). ++ */ ++ eth_skb_pad(skb); ++ + /* Build the special tag after the MAC Source Address. If VLAN header + * is present, it's required that VLAN header and special tag is + * being combined. Only in this way we can allow the switch can parse diff --git a/target/linux/generic/pending-6.12/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch b/target/linux/generic/pending-6.12/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch new file mode 100644 index 00000000000..dea84c70797 --- /dev/null +++ b/target/linux/generic/pending-6.12/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch @@ -0,0 +1,57 @@ +From 4e432e530db0056450fbc4a3cee793f16adc39a7 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 8 Oct 2024 23:58:41 +0100 +Subject: [PATCH] net: phy: populate host_interfaces when attaching PHY + +Use bitmask of interfaces supported by the MAC for the PHY to choose +from if the declared interface mode is among those using a single pair +of SerDes lanes. +This will allow 2500Base-T PHYs to switch to SGMII on most hosts, which +results in half-duplex being supported in case the MAC supports them. +Without this change, 2500Base-T PHYs will always operate in 2500Base-X +mode with rate-matching, which is not only wasteful in terms of energy +consumption, but also limits the supported interface modes to +full-duplex only. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/phylink.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -2273,7 +2273,7 @@ int phylink_fwnode_phy_connect(struct ph + { + struct fwnode_handle *phy_fwnode; + struct phy_device *phy_dev; +- int ret; ++ int i, ret; + + /* Fixed links and 802.3z are handled without needing a PHY */ + if (pl->cfg_link_an_mode == MLO_AN_FIXED || +@@ -2303,6 +2303,25 @@ int phylink_fwnode_phy_connect(struct ph + if (pl->config->mac_requires_rxc) + flags |= PHY_F_RXC_ALWAYS_ON; + ++ /* Assume single-lane SerDes interface modes share the same ++ * lanes and allow the PHY to switch to slower also supported modes ++ */ ++ for (i = ARRAY_SIZE(phylink_sfp_interface_preference) - 1; i >= 0; i--) { ++ /* skip unsupported modes */ ++ if (!test_bit(phylink_sfp_interface_preference[i], pl->config->supported_interfaces)) ++ continue; ++ ++ __set_bit(phylink_sfp_interface_preference[i], phy_dev->host_interfaces); ++ ++ /* skip all faster modes */ ++ if (phylink_sfp_interface_preference[i] == pl->link_interface) ++ break; ++ } ++ ++ if (test_bit(pl->link_interface, phylink_sfp_interfaces)) ++ phy_interface_and(phy_dev->host_interfaces, phylink_sfp_interfaces, ++ pl->config->supported_interfaces); ++ + ret = phy_attach_direct(pl->netdev, phy_dev, flags, + pl->link_interface); + phy_device_free(phy_dev); diff --git a/target/linux/generic/pending-6.12/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch b/target/linux/generic/pending-6.12/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch new file mode 100644 index 00000000000..2f90df60131 --- /dev/null +++ b/target/linux/generic/pending-6.12/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch @@ -0,0 +1,174 @@ +From: Felix Fietkau +Date: Fri, 27 Aug 2021 12:22:32 +0200 +Subject: [PATCH] bridge: add knob for filtering rx/tx BPDU packets on a port + +Some devices (e.g. wireless APs) can't have devices behind them be part of +a bridge topology with redundant links, due to address limitations. +Additionally, broadcast traffic on these devices is somewhat expensive, due to +the low data rate and wakeups of clients in powersave mode. +This knob can be used to ensure that BPDU packets are never sent or forwarded +to/from these devices + +Signed-off-by: Felix Fietkau +--- + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -61,6 +61,7 @@ struct br_ip_list { + #define BR_PORT_LOCKED BIT(21) + #define BR_PORT_MAB BIT(22) + #define BR_NEIGH_VLAN_SUPPRESS BIT(23) ++#define BR_BPDU_FILTER BIT(24) + + #define BR_DEFAULT_AGEING_TIME (300 * HZ) + +--- a/net/bridge/br_forward.c ++++ b/net/bridge/br_forward.c +@@ -202,6 +202,7 @@ void br_flood(struct net_bridge *br, str + enum br_pkt_type pkt_type, bool local_rcv, bool local_orig, + u16 vid) + { ++ const unsigned char *dest = eth_hdr(skb)->h_dest; + struct net_bridge_port *prev = NULL; + struct net_bridge_port *p; + +@@ -219,6 +220,10 @@ void br_flood(struct net_bridge *br, str + case BR_PKT_MULTICAST: + if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev) + continue; ++ if ((p->flags & BR_BPDU_FILTER) && ++ unlikely(is_link_local_ether_addr(dest) && ++ dest[5] == 0)) ++ continue; + break; + case BR_PKT_BROADCAST: + if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev) +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -368,6 +368,8 @@ static rx_handler_result_t br_handle_fra + fwd_mask |= p->group_fwd_mask; + switch (dest[5]) { + case 0x00: /* Bridge Group Address */ ++ if (p->flags & BR_BPDU_FILTER) ++ goto drop; + /* If STP is turned off, + then must forward to keep loop detection */ + if (p->br->stp_enabled == BR_NO_STP || +--- a/net/bridge/br_sysfs_if.c ++++ b/net/bridge/br_sysfs_if.c +@@ -240,6 +240,7 @@ BRPORT_ATTR_FLAG(multicast_flood, BR_MCA + BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD); + BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS); + BRPORT_ATTR_FLAG(isolated, BR_ISOLATED); ++BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER); + + #ifdef CONFIG_BRIDGE_IGMP_SNOOPING + static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) +@@ -292,6 +293,7 @@ static const struct brport_attribute *br + &brport_attr_group_fwd_mask, + &brport_attr_neigh_suppress, + &brport_attr_isolated, ++ &brport_attr_bpdu_filter, + &brport_attr_backup_port, + NULL + }; +--- a/net/bridge/br_stp_bpdu.c ++++ b/net/bridge/br_stp_bpdu.c +@@ -80,7 +80,8 @@ void br_send_config_bpdu(struct net_brid + { + unsigned char buf[35]; + +- if (p->br->stp_enabled != BR_KERNEL_STP) ++ if (p->br->stp_enabled != BR_KERNEL_STP || ++ (p->flags & BR_BPDU_FILTER)) + return; + + buf[0] = 0; +@@ -127,7 +128,8 @@ void br_send_tcn_bpdu(struct net_bridge_ + { + unsigned char buf[4]; + +- if (p->br->stp_enabled != BR_KERNEL_STP) ++ if (p->br->stp_enabled != BR_KERNEL_STP || ++ (p->flags & BR_BPDU_FILTER)) + return; + + buf[0] = 0; +@@ -172,6 +174,9 @@ void br_stp_rcv(const struct stp_proto * + if (!(br->dev->flags & IFF_UP)) + goto out; + ++ if (p->flags & BR_BPDU_FILTER) ++ goto out; ++ + if (p->state == BR_STATE_DISABLED) + goto out; + +--- a/include/uapi/linux/if_link.h ++++ b/include/uapi/linux/if_link.h +@@ -1094,6 +1094,7 @@ enum { + IFLA_BRPORT_MCAST_MAX_GROUPS, + IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, + IFLA_BRPORT_BACKUP_NHID, ++ IFLA_BRPORT_BPDU_FILTER, + __IFLA_BRPORT_MAX + }; + #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) +--- a/net/bridge/br_netlink.c ++++ b/net/bridge/br_netlink.c +@@ -190,6 +190,7 @@ static inline size_t br_port_info_size(v + + nla_total_size(1) /* IFLA_BRPORT_LOCKED */ + + nla_total_size(1) /* IFLA_BRPORT_MAB */ + + nla_total_size(1) /* IFLA_BRPORT_NEIGH_VLAN_SUPPRESS */ ++ + nla_total_size(1) /* IFLA_BRPORT_BPDU_FILTER */ + + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */ + + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */ + + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */ +@@ -282,7 +283,8 @@ static int br_port_fill_attrs(struct sk_ + nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED)) || + nla_put_u8(skb, IFLA_BRPORT_MAB, !!(p->flags & BR_PORT_MAB)) || + nla_put_u8(skb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, +- !!(p->flags & BR_NEIGH_VLAN_SUPPRESS))) ++ !!(p->flags & BR_NEIGH_VLAN_SUPPRESS)) || ++ nla_put_u8(skb, IFLA_BRPORT_BPDU_FILTER, !!(p->flags & BR_BPDU_FILTER))) + return -EMSGSIZE; + + timerval = br_timer_value(&p->message_age_timer); +@@ -902,6 +904,7 @@ static const struct nla_policy br_port_p + [IFLA_BRPORT_MCAST_MAX_GROUPS] = { .type = NLA_U32 }, + [IFLA_BRPORT_NEIGH_VLAN_SUPPRESS] = NLA_POLICY_MAX(NLA_U8, 1), + [IFLA_BRPORT_BACKUP_NHID] = { .type = NLA_U32 }, ++ [IFLA_BRPORT_BPDU_FILTER] = { .type = NLA_U8 }, + }; + + /* Change the state of the port and notify spanning tree */ +@@ -970,6 +973,7 @@ static int br_setport(struct net_bridge_ + br_set_port_flag(p, tb, IFLA_BRPORT_MAB, BR_PORT_MAB); + br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, + BR_NEIGH_VLAN_SUPPRESS); ++ br_set_port_flag(p, tb, IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER); + + if ((p->flags & BR_PORT_MAB) && + (!(p->flags & BR_PORT_LOCKED) || !(p->flags & BR_LEARNING))) { +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -62,7 +62,7 @@ + #include "dev.h" + + #define RTNL_MAX_TYPE 50 +-#define RTNL_SLAVE_MAX_TYPE 44 ++#define RTNL_SLAVE_MAX_TYPE 45 + + struct rtnl_link { + rtnl_doit_func doit; +@@ -5009,7 +5009,9 @@ int ndo_dflt_bridge_getlink(struct sk_bu + brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD) || + brport_nla_put_flag(skb, flags, mask, +- IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD)) { ++ IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD) || ++ brport_nla_put_flag(skb, flags, mask, ++ IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER)) { + nla_nest_cancel(skb, protinfo); + goto nla_put_failure; + } diff --git a/target/linux/generic/pending-6.12/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch b/target/linux/generic/pending-6.12/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch new file mode 100644 index 00000000000..0e8915903f7 --- /dev/null +++ b/target/linux/generic/pending-6.12/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch @@ -0,0 +1,86 @@ +From 3b4329230db8750bea7a56ef07f07cbbf5fc6c5a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 4 Jul 2023 22:50:12 +0200 +Subject: [PATCH 19/20] net: dsa: qca8k: implement lag_fdb_add/del ops + +Implement lag_fdb_add/del ops to correctly support using LAG interface. +Qca8k switch supports declaring fdb entry for link aggregation by simply +setting the DES_PORT bits to all the LAG member. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 2 ++ + drivers/net/dsa/qca/qca8k-common.c | 48 ++++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 6 ++++ + 3 files changed, 56 insertions(+) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -2031,6 +2031,8 @@ static const struct dsa_switch_ops qca8k + .port_fdb_add = qca8k_port_fdb_add, + .port_fdb_del = qca8k_port_fdb_del, + .port_fdb_dump = qca8k_port_fdb_dump, ++ .lag_fdb_add = qca8k_lag_fdb_add, ++ .lag_fdb_del = qca8k_lag_fdb_del, + .port_mdb_add = qca8k_port_mdb_add, + .port_mdb_del = qca8k_port_mdb_del, + .port_mirror_add = qca8k_port_mirror_add, +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -1234,6 +1234,42 @@ int qca8k_port_lag_leave(struct dsa_swit + return qca8k_lag_refresh_portmap(ds, port, lag, true); + } + ++int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct dsa_port *dp; ++ u16 port_mask = 0; ++ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ dsa_lag_foreach_port(dp, ds->dst, &lag) ++ port_mask |= BIT(dp->index); ++ ++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid); ++} ++ ++int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct dsa_port *dp; ++ u16 port_mask = 0; ++ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ dsa_lag_foreach_port(dp, ds->dst, &lag) ++ port_mask |= BIT(dp->index); ++ ++ return qca8k_fdb_del(priv, addr, port_mask, vid); ++} ++ + int qca8k_read_switch_id(struct qca8k_priv *priv) + { + u32 val; +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -592,5 +592,11 @@ int qca8k_port_lag_join(struct dsa_switc + struct netlink_ext_ack *extack); + int qca8k_port_lag_leave(struct dsa_switch *ds, int port, + struct dsa_lag lag); ++int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db); ++int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db); + + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/pending-6.12/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch b/target/linux/generic/pending-6.12/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch new file mode 100644 index 00000000000..d10bbcf8029 --- /dev/null +++ b/target/linux/generic/pending-6.12/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch @@ -0,0 +1,37 @@ +From b954d61d9ecfa64450fc178586719dc2a95b92a7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 20 Jun 2023 21:48:24 +0200 +Subject: [PATCH 3/4] net: dsa: qca8k: enable flooding to both CPU port + +To permit a multi-CPU setup, flood all unknown frames to all CPU ports. +Each CPU port should have correct LOOKUP MEMBER configuration to +prevent receiving duplicate packets from user ports. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1913,15 +1913,12 @@ qca8k_setup(struct dsa_switch *ds) + } + } + +- /* Forward all unknown frames to CPU port for Linux processing +- * Notice that in multi-cpu config only one port should be set +- * for igmp, unknown, multicast and broadcast packet +- */ ++ /* Forward all unknown frames to CPU port for Linux processing */ + ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, dsa_cpu_ports(ds)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, dsa_cpu_ports(ds)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, dsa_cpu_ports(ds)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, dsa_cpu_ports(ds))); + if (ret) + return ret; + diff --git a/target/linux/generic/pending-6.12/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch b/target/linux/generic/pending-6.12/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch new file mode 100644 index 00000000000..25c6ae3f78b --- /dev/null +++ b/target/linux/generic/pending-6.12/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch @@ -0,0 +1,158 @@ +From b2d6ebf2f92f8695c83fa6979f4ab579c588df76 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 20 Jun 2023 07:57:38 +0200 +Subject: [PATCH 4/4] net: dsa: qca8k: add support for port_change_master + +Add support for port_change_master to permit assigning an alternative +CPU port if the switch have both CPU port connected or create a LAG on +both CPU port and assign the LAG as DSA master. + +On port change master request, we check if the master is a LAG. +With LAG we compose the cpu_port_mask with the CPU port in the LAG, if +master is a simple dsa_port, we derive the index. + +Finally we apply the new cpu_port_mask to the LOOKUP MEMBER to permit +the port to receive packet by the new CPU port setup for the port and we +refresh the CPU ports LOOKUP MEMBER configuration to reflect the new +user port state. + +port_lag_join/leave is updated to refresh the user ports if we detect +that the LAG is a DSA master and we have user port using it as a master. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 116 ++++++++++++++++++++++++++++++- + 1 file changed, 114 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1750,6 +1750,117 @@ qca8k_get_tag_protocol(struct dsa_switch + return DSA_TAG_PROTO_QCA; + } + ++static int qca8k_port_change_master(struct dsa_switch *ds, int port, ++ struct net_device *master, ++ struct netlink_ext_ack *extack) ++{ ++ struct dsa_switch_tree *dst = ds->dst; ++ struct qca8k_priv *priv = ds->priv; ++ u8 cpu_port_mask = 0; ++ struct dsa_port *dp; ++ u32 val; ++ int ret; ++ ++ /* With LAG of CPU port, compose the mask for port LOOKUP MEMBER */ ++ if (netif_is_lag_master(master)) { ++ struct dsa_lag *lag; ++ int id; ++ ++ id = dsa_lag_id(dst, master); ++ lag = dsa_lag_by_id(dst, id); ++ ++ dsa_lag_foreach_port(dp, dst, lag) ++ if (dsa_port_is_cpu(dp)) ++ cpu_port_mask |= BIT(dp->index); ++ } else { ++ dp = master->dsa_ptr; ++ cpu_port_mask |= BIT(dp->index); ++ } ++ ++ /* Connect port to new cpu port */ ++ ret = regmap_read(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), &val); ++ if (ret) ++ return ret; ++ ++ /* Reset connected CPU port in port LOOKUP MEMBER */ ++ val &= ~dsa_cpu_ports(ds); ++ /* Assign the new CPU port in port LOOKUP MEMBER */ ++ val |= cpu_port_mask; ++ ++ ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, ++ val); ++ if (ret) ++ return ret; ++ ++ /* Refresh CPU port LOOKUP MEMBER with new port */ ++ dsa_tree_for_each_cpu_port(dp, ds->dst) { ++ u32 reg = QCA8K_PORT_LOOKUP_CTRL(dp->index); ++ ++ /* If CPU port in mask assign port, else remove port */ ++ if (BIT(dp->index) & cpu_port_mask) ++ ret = regmap_set_bits(priv->regmap, reg, BIT(port)); ++ else ++ ret = regmap_clear_bits(priv->regmap, reg, BIT(port)); ++ ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qca8k_port_lag_refresh_user_ports(struct dsa_switch *ds, ++ struct dsa_lag lag) ++{ ++ struct net_device *lag_dev = lag.dev; ++ struct dsa_port *dp; ++ int ret; ++ ++ /* Ignore if LAG is not a DSA master */ ++ if (!netif_is_lag_master(lag_dev)) ++ return 0; ++ ++ dsa_switch_for_each_user_port(dp, ds) { ++ /* Skip if assigned master is not the LAG */ ++ if (dsa_port_to_conduit(dp) != lag_dev) ++ continue; ++ ++ ret = qca8k_port_change_master(ds, dp->index, ++ lag_dev, NULL); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qca8xxx_port_lag_join(struct dsa_switch *ds, int port, ++ struct dsa_lag lag, ++ struct netdev_lag_upper_info *info, ++ struct netlink_ext_ack *extack) ++{ ++ int ret; ++ ++ ret = qca8k_port_lag_join(ds, port, lag, info, extack); ++ if (ret) ++ return ret; ++ ++ return qca8k_port_lag_refresh_user_ports(ds, lag); ++} ++ ++static int qca8xxx_port_lag_leave(struct dsa_switch *ds, int port, ++ struct dsa_lag lag) ++{ ++ int ret; ++ ++ ret = qca8k_port_lag_leave(ds, port, lag); ++ if (ret) ++ return ret; ++ ++ return qca8k_port_lag_refresh_user_ports(ds, lag); ++} ++ + static void + qca8k_conduit_change(struct dsa_switch *ds, const struct net_device *conduit, + bool operational) +@@ -2039,8 +2150,9 @@ static const struct dsa_switch_ops qca8k + .port_vlan_del = qca8k_port_vlan_del, + .phylink_get_caps = qca8k_phylink_get_caps, + .get_phy_flags = qca8k_get_phy_flags, +- .port_lag_join = qca8k_port_lag_join, +- .port_lag_leave = qca8k_port_lag_leave, ++ .port_lag_join = qca8xxx_port_lag_join, ++ .port_lag_leave = qca8xxx_port_lag_leave, ++ .port_change_conduit = qca8k_port_change_master, + .conduit_state_change = qca8k_conduit_change, + .connect_tag_protocol = qca8k_connect_tag_protocol, + }; diff --git a/target/linux/generic/pending-6.12/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-6.12/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch new file mode 100644 index 00000000000..03cf2db6b60 --- /dev/null +++ b/target/linux/generic/pending-6.12/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch @@ -0,0 +1,57 @@ +From 0f6599167c126ce32c85d4f8a1f3d1775a268572 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 6 Oct 2023 12:44:00 +0200 +Subject: [PATCH] net: dsa: qca8k: enable assisted learning on CPU port + +Enable assisted learning on CPU port. + +It has been verified that there is a problem in packet roaming +from one BSS to another in the same security settings from one +physical R7800 to another physical R7800 where they are in the +same L2 broadcast domain backhauled/linked together via one +of the ethernet ports. +DHCP will fail to complete and traffic cannot flow for around 300 +seconds. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -2022,6 +2022,12 @@ qca8k_setup(struct dsa_switch *ds) + dev_err(priv->dev, "failed enabling QCA header mode on port %d", dp->index); + return ret; + } ++ ++ /* Disable learning by default on all ports */ ++ ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(dp->index), ++ QCA8K_PORT_LOOKUP_LEARN); ++ if (ret) ++ return ret; + } + + /* Forward all unknown frames to CPU port for Linux processing */ +@@ -2051,11 +2057,6 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + +- ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_LEARN); +- if (ret) +- return ret; +- + /* For port based vlans to work we need to set the + * default egress vid + */ +@@ -2107,6 +2108,9 @@ qca8k_setup(struct dsa_switch *ds) + /* Set max number of LAGs supported */ + ds->num_lag_ids = QCA8K_NUM_LAGS; + ++ /* HW learn on CPU port is limited and require manual setting */ ++ ds->assisted_learning_on_cpu_port = true; ++ + return 0; + } + diff --git a/target/linux/generic/pending-6.12/713-net-phy-c45-check-validity-of-10GbE-link-partner.patch b/target/linux/generic/pending-6.12/713-net-phy-c45-check-validity-of-10GbE-link-partner.patch new file mode 100644 index 00000000000..5870dfb2ba3 --- /dev/null +++ b/target/linux/generic/pending-6.12/713-net-phy-c45-check-validity-of-10GbE-link-partner.patch @@ -0,0 +1,29 @@ +From 7d719484930bc10c8472c0d43b94b68087541b24 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 22 Apr 2023 01:25:39 +0100 +Subject: [PATCH] net: phy: c45: check validity of 10GbE link-partner + advertisement + +Only use link-partner advertisement bits for 10GbE modes if they are +actually valid. Check LOCALOK and REMOTEOK bits and clear 10GbE modes +unless both of them are set. + +Signed-off-by: Daniel Golle +--- a/include/linux/mdio.h ++++ b/include/linux/mdio.h +@@ -323,8 +323,14 @@ static inline u32 linkmode_adv_to_mii_10 + * to linkmode advertisement settings. Other bits in advertising aren't changed. + */ + static inline void mii_10gbt_stat_mod_linkmode_lpa_t(unsigned long *advertising, +- u32 lpa) ++ u32 lpa_in) + { ++ u32 lpa = lpa_in; ++ ++ if (!(lpa & MDIO_AN_10GBT_STAT_REMOK) || ++ !(lpa & MDIO_AN_10GBT_STAT_LOCOK)) ++ lpa = 0; ++ + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + advertising, lpa & MDIO_AN_10GBT_STAT_LP2_5G); + linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, diff --git a/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch b/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch new file mode 100644 index 00000000000..2a0a0e953b5 --- /dev/null +++ b/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch @@ -0,0 +1,57 @@ +From 85cd45580f5e3b26068cccb7d6173f200e754dc0 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 2 Apr 2023 23:56:16 +0100 +Subject: [PATCH 1/2] net: phy: realtek: use genphy_soft_reset for 2.5G PHYs + +Some vendor bootloaders do weird things with those PHYs which result in +link modes being reported wrongly. Start from a clean sheet by resetting +the PHY. + +Reported-by: Yevhen Kolomeiko +Signed-off-by: Daniel Golle +--- + drivers/net/phy/realtek/realtek_main.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -2255,6 +2255,7 @@ static struct phy_driver realtek_drvs[] + }, { + .name = "RTL8226 2.5Gbps PHY", + .match_phy_device = rtl8226_match_phy_device, ++ .soft_reset = genphy_soft_reset, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .read_status = rtl822x_read_status, +@@ -2267,6 +2268,7 @@ static struct phy_driver realtek_drvs[] + }, { + .match_phy_device = rtl8221b_match_phy_device, + .name = "RTL8226B_RTL8221B 2.5Gbps PHY", ++ .soft_reset = genphy_soft_reset, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, +@@ -2297,6 +2299,7 @@ static struct phy_driver realtek_drvs[] + }, { + PHY_ID_MATCH_EXACT(0x001cc848), + .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", ++ .soft_reset = genphy_soft_reset, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, +@@ -2315,6 +2318,7 @@ static struct phy_driver realtek_drvs[] + .name = "RTL8221B-VB-CG 2.5Gbps PHY", + .config_intr = rtl8221b_config_intr, + .handle_interrupt = rtl8221b_handle_interrupt, ++ .soft_reset = rtl822x_c45_soft_reset, + .probe = rtl822x_probe, + .config_init = rtl822xb_config_init, + .inband_caps = rtl822x_inband_caps, +@@ -2334,6 +2338,7 @@ static struct phy_driver realtek_drvs[] + .name = "RTL8221B-VM-CG 2.5Gbps PHY", + .config_intr = rtl8221b_config_intr, + .handle_interrupt = rtl8221b_handle_interrupt, ++ .soft_reset = rtl822x_c45_soft_reset, + .probe = rtl822x_probe, + .config_init = rtl822xb_config_init, + .inband_caps = rtl822x_inband_caps, diff --git a/target/linux/generic/pending-6.12/720-03-net-phy-realtek-make-sure-paged-read-is-protected-by.patch b/target/linux/generic/pending-6.12/720-03-net-phy-realtek-make-sure-paged-read-is-protected-by.patch new file mode 100644 index 00000000000..8a0db2c2ea3 --- /dev/null +++ b/target/linux/generic/pending-6.12/720-03-net-phy-realtek-make-sure-paged-read-is-protected-by.patch @@ -0,0 +1,35 @@ +From 4dd2cc9b91ecb25f278a2c55e07e6455e9000e6b Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 22 Apr 2023 01:21:14 +0100 +Subject: [PATCH] net: phy: realtek: make sure paged read is protected by mutex + +As we cannot rely on phy_read_paged function before the PHY is +identified, the paged read in rtlgen_supports_2_5gbps needs to be open +coded as it is being called by the match_phy_device function, ie. before +.read_page and .write_page have been populated. + +Make sure it is also protected by the MDIO bus mutex and use +rtl821x_write_page instead of 3 individually locked MDIO bus operations. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/realtek/realtek_main.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1823,9 +1823,11 @@ static bool rtlgen_supports_2_5gbps(stru + { + int val; + +- phy_write(phydev, RTL821x_PAGE_SELECT, 0xa61); +- val = phy_read(phydev, 0x13); +- phy_write(phydev, RTL821x_PAGE_SELECT, 0); ++ mutex_lock(&phydev->mdio.bus->mdio_lock); ++ rtl821x_write_page(phydev, 0xa61); ++ val = __phy_read(phydev, 0x13); ++ rtl821x_write_page(phydev, 0); ++ mutex_unlock(&phydev->mdio.bus->mdio_lock); + + return val >= 0 && val & MDIO_PMA_SPEED_2_5G; + } diff --git a/target/linux/generic/pending-6.12/720-04-net-phy-realtek-setup-aldps.patch b/target/linux/generic/pending-6.12/720-04-net-phy-realtek-setup-aldps.patch new file mode 100644 index 00000000000..1a33ea5bc67 --- /dev/null +++ b/target/linux/generic/pending-6.12/720-04-net-phy-realtek-setup-aldps.patch @@ -0,0 +1,93 @@ +From 9155098547fb1172d4fa536f3f6bc9d42f59d08c Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 22 Apr 2023 03:26:01 +0100 +Subject: [PATCH] net: phy: realtek: setup ALDPS on RTL822x + +Setup Link Down Power Saving Mode according the DTS property +just like for RTL821x 1GE PHYs. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/realtek/realtek_main.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -170,6 +170,10 @@ + + #define RTL8224_SRAM_RTCT_LEN(pair) (0x8028 + (pair) * 4) + ++#define RTL8221B_PHYCR1 0xa430 ++#define RTL8221B_PHYCR1_ALDPS_EN BIT(2) ++#define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN BIT(12) ++ + #define RTL8366RB_POWER_SAVE 0x15 + #define RTL8366RB_POWER_SAVE_ON BIT(12) + +@@ -216,6 +220,10 @@ struct rtl821x_priv { + u16 iner; + }; + ++struct rtl822x_priv { ++ bool enable_aldps; ++}; ++ + static int rtl821x_read_page(struct phy_device *phydev) + { + return __phy_read(phydev, RTL821x_PAGE_SELECT); +@@ -1238,6 +1246,18 @@ static int rtl822x_write_mmd(struct phy_ + + static int rtl822x_probe(struct phy_device *phydev) + { ++ struct device *dev = &phydev->mdio.dev; ++ struct rtl822x_priv *priv; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->enable_aldps = of_property_read_bool(dev->of_node, ++ "realtek,aldps-enable"); ++ ++ phydev->priv = priv; ++ + if (IS_ENABLED(CONFIG_REALTEK_PHY_HWMON) && + phydev->phy_id != RTL_GENERIC_PHYID) + return rtl822x_hwmon_init(phydev); +@@ -1328,6 +1348,19 @@ static int rtl822xb_write_mmd(struct phy + return write_ret; + } + ++static int rtl822x_init_phycr1(struct phy_device *phydev, bool no_aldps) ++{ ++ struct rtl822x_priv *priv = phydev->priv; ++ u16 val = 0; ++ ++ if (priv->enable_aldps && !no_aldps) ++ val = RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN; ++ ++ return phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL8221B_PHYCR1, ++ RTL8221B_PHYCR1_ALDPS_EN | ++ RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN, val); ++} ++ + static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1) + { + bool has_2500, has_sgmii; +@@ -1385,7 +1418,15 @@ static int rtl822x_set_serdes_option_mod + if (ret < 0) + return ret; + +- return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020); ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020); ++ if (ret < 0) ++ return ret; ++ ++ ret = rtl822x_init_phycr1(phydev, false); ++ if (ret < 0) ++ return ret; ++ ++ return 0; + } + + static int rtl822x_config_init(struct phy_device *phydev) diff --git a/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch new file mode 100644 index 00000000000..829f4b93019 --- /dev/null +++ b/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch @@ -0,0 +1,52 @@ +From 0de82310d2b32e78ff79d42c08b1122a6ede3778 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 30 Apr 2023 00:15:41 +0100 +Subject: [PATCH] net: phy: realtek: detect early version of RTL8221B + +Early versions (?) of the RTL8221B PHY cannot be identified in a regular +Clause-45 bus scan as the PHY doesn't report the implemented MMDs +correctly but returns 0 instead. +Implement custom identify function using the PKGID instead of iterating +over the implemented MMDs. + +Signed-off-by: Daniel Golle +[forward-port by @namiltd] +Signed-off-by: Mieczyslaw Nalewaj +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1908,10 +1908,32 @@ static int rtl8226_match_phy_device(stru + static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id, + bool is_c45) + { +- if (phydev->is_c45) +- return is_c45 && (id == phydev->c45_ids.device_ids[1]); +- else ++ if (phydev->is_c45) { ++ u32 rid; ++ ++ if (!is_c45) ++ return 0; ++ ++ rid = phydev->c45_ids.device_ids[1]; ++ if ((rid == 0xffffffff) && phydev->mdio.bus->read_c45) { ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID1); ++ if (val < 0) ++ return 0; ++ ++ rid = val << 16; ++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID2); ++ if (val < 0) ++ return 0; ++ ++ rid |= val; ++ } ++ ++ return (id == rid); ++ } else { + return !is_c45 && (id == phydev->phy_id); ++ } + } + + static int rtl8221b_match_phy_device(struct phy_device *phydev, diff --git a/target/linux/generic/pending-6.12/720-06-net-phy-realtek-mark-existing-MMDs-as-present.patch b/target/linux/generic/pending-6.12/720-06-net-phy-realtek-mark-existing-MMDs-as-present.patch new file mode 100644 index 00000000000..922d3f4a873 --- /dev/null +++ b/target/linux/generic/pending-6.12/720-06-net-phy-realtek-mark-existing-MMDs-as-present.patch @@ -0,0 +1,27 @@ +From 1addfb042a9d27788a0fb2c2935045b56fd8560e Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 23 Jan 2025 03:25:29 +0000 +Subject: [PATCH] net: phy: realtek: mark existing MMDs as present + +When using Clause-45 mode to access RealTek RTL8221B 2.5G PHYs some +versions of the PHY fail to report the MMDs present on the PHY. +Mark MMDs PMAPMD, PCS and AN which are always existing according to +the datasheet as present to fix that. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/realtek/realtek_main.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1604,6 +1604,9 @@ static int rtl822x_c45_get_features(stru + linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, + phydev->supported); + ++ phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | ++ MDIO_DEVS_AN; ++ + return genphy_c45_pma_read_abilities(phydev); + } + diff --git a/target/linux/generic/pending-6.12/720-07-net-phy-realtek-disable-MDIO-broadcast.patch b/target/linux/generic/pending-6.12/720-07-net-phy-realtek-disable-MDIO-broadcast.patch new file mode 100644 index 00000000000..e37faaca43f --- /dev/null +++ b/target/linux/generic/pending-6.12/720-07-net-phy-realtek-disable-MDIO-broadcast.patch @@ -0,0 +1,33 @@ +From: Daniel Golle +Date: Thu, 30 Jan 2025 05:38:31 +0000 +Subject: [PATCH] net: phy: realtek: disable MDIO broadcast + +RealTek's PHYs by default also listen on MDIO address 0 which is defined +as broadcast address. This can lead to problems if there is an actual PHY +(such as MT7981 built-in PHY) present at this address, as accessing that +PHY may then confuse the RealTek PHY. + +Disabled listening on the MDIO broadcast address to avoid such problems. + +Signed-off-by: Daniel Golle +--- +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -173,6 +173,7 @@ + #define RTL8221B_PHYCR1 0xa430 + #define RTL8221B_PHYCR1_ALDPS_EN BIT(2) + #define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN BIT(12) ++#define RTL8221B_PHYCR1_PHYAD_0_EN BIT(13) + + #define RTL8366RB_POWER_SAVE 0x15 + #define RTL8366RB_POWER_SAVE_ON BIT(12) +@@ -1358,7 +1359,8 @@ static int rtl822x_init_phycr1(struct ph + + return phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL8221B_PHYCR1, + RTL8221B_PHYCR1_ALDPS_EN | +- RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN, val); ++ RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN | ++ RTL8221B_PHYCR1_PHYAD_0_EN, val); + } + + static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1) diff --git a/target/linux/generic/pending-6.12/720-08-net-phy-realtek-rate-adapter-in-C22-mode.patch b/target/linux/generic/pending-6.12/720-08-net-phy-realtek-rate-adapter-in-C22-mode.patch new file mode 100644 index 00000000000..f8a28d570b6 --- /dev/null +++ b/target/linux/generic/pending-6.12/720-08-net-phy-realtek-rate-adapter-in-C22-mode.patch @@ -0,0 +1,22 @@ +From: Daniel Golle +Date: Mon, 5 Jan 2026 17:55:02 +0000 +Subject: [PATCH] net: phy: realtek: rate-adapter in C22 mode + +Use rate-adapter mode in case the PHY is connected to the host using +Clause-22 MDIO. + +This is necessary because phylink only handles dynamically switching the +interface mode if the PHY is connected using Clause-45 MDIO. + +Signed-off-by: Daniel Golle +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1387,7 +1387,7 @@ static int rtl822x_set_serdes_option_mod + return 0; + + /* determine SerDes option mode */ +- if (has_2500 && !has_sgmii) { ++ if (has_2500 && (!has_sgmii || !phydev->is_c45)) { + mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX; + phydev->rate_matching = RATE_MATCH_PAUSE; + } else { diff --git a/target/linux/generic/pending-6.12/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch b/target/linux/generic/pending-6.12/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch new file mode 100644 index 00000000000..50d3403772d --- /dev/null +++ b/target/linux/generic/pending-6.12/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch @@ -0,0 +1,59 @@ +From 686c603f67ae87bf21a61b5e4b1564443f41c3ee Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 20 Oct 2022 03:34:43 +0200 +Subject: [PATCH] net: permit ieee80211_ptr even with no CFG82111 support + +Introduce a new flag CONFIG_CFG80211_HEADERS to compile in ieee80211_ptr +even if CFG80211 support is not compiled in. This is needed for the +backports project and for any downstream wireless driver that loads in +the kernel dynamically. + +Signed-off-by: Christian Marangi +--- + include/linux/netdevice.h | 2 +- + net/batman-adv/hard-interface.c | 2 +- + net/wireless/Kconfig | 4 ++++ + 3 files changed, 6 insertions(+), 2 deletions(-) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -2224,7 +2224,7 @@ struct net_device { + #if IS_ENABLED(CONFIG_AX25) + struct ax25_dev __rcu *ax25_ptr; + #endif +-#if IS_ENABLED(CONFIG_CFG80211) ++#if IS_ENABLED(CONFIG_CFG80211_HEADERS) + struct wireless_dev *ieee80211_ptr; + #endif + #if IS_ENABLED(CONFIG_IEEE802154) || IS_ENABLED(CONFIG_6LOWPAN) +--- a/net/batman-adv/hard-interface.c ++++ b/net/batman-adv/hard-interface.c +@@ -309,7 +309,7 @@ static bool batadv_is_cfg80211_netdev(st + if (!net_device) + return false; + +-#if IS_ENABLED(CONFIG_CFG80211) ++#if IS_ENABLED(CONFIG_CFG80211_HEADERS) + /* cfg80211 drivers have to set ieee80211_ptr */ + if (net_device->ieee80211_ptr) + return true; +--- a/net/wireless/Kconfig ++++ b/net/wireless/Kconfig +@@ -26,6 +26,7 @@ config CFG80211 + # using a different algorithm, though right now they shouldn't + # (this is here rather than below to allow it to be a module) + select CRYPTO_SHA256 if CFG80211_USE_KERNEL_REGDB_KEYS ++ select CFG80211_HEADERS + help + cfg80211 is the Linux wireless LAN (802.11) configuration API. + Enable this if you have a wireless device. +@@ -36,6 +37,9 @@ config CFG80211 + + When built as a module it will be called cfg80211. + ++config CFG80211_HEADERS ++ bool "cfg80211 - headers support" ++ + if CFG80211 + + config NL80211_TESTMODE diff --git a/target/linux/generic/pending-6.12/732-00-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch b/target/linux/generic/pending-6.12/732-00-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch new file mode 100644 index 00000000000..ec6ed6e035d --- /dev/null +++ b/target/linux/generic/pending-6.12/732-00-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch @@ -0,0 +1,44 @@ +From: Felix Fietkau +Date: Thu, 27 Oct 2022 23:39:52 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: compile out netsys v2 code + on mt7621 + +Avoid some branches in the hot path on low-end devices with limited CPU power, +and reduce code size + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1344,6 +1344,22 @@ struct mtk_mac { + /* the struct describing the SoC. these are declared in the soc_xyz.c files */ + extern const struct of_device_id of_mtk_match[]; + ++#ifdef CONFIG_SOC_MT7621 ++static inline bool mtk_is_netsys_v1(struct mtk_eth *eth) ++{ ++ return true; ++} ++ ++static inline bool mtk_is_netsys_v2_or_greater(struct mtk_eth *eth) ++{ ++ return false; ++} ++ ++static inline bool mtk_is_netsys_v3_or_greater(struct mtk_eth *eth) ++{ ++ return false; ++} ++#else + static inline bool mtk_is_netsys_v1(struct mtk_eth *eth) + { + return eth->soc->version == 1; +@@ -1358,6 +1374,7 @@ static inline bool mtk_is_netsys_v3_or_g + { + return eth->soc->version > 2; + } ++#endif + + static inline struct mtk_foe_entry * + mtk_foe_get_entry(struct mtk_ppe *ppe, u16 hash) diff --git a/target/linux/generic/pending-6.12/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch b/target/linux/generic/pending-6.12/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch new file mode 100644 index 00000000000..71b20b9c960 --- /dev/null +++ b/target/linux/generic/pending-6.12/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch @@ -0,0 +1,102 @@ +From: Felix Fietkau +Date: Thu, 3 Nov 2022 12:38:49 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: work around issue with sending + small fragments + +When lots of frames are sent with a number of very small fragments, an +internal FIFO can overflow, causing the DMA engine to lock up lock up and +transmit attempts time out. + +Fix this on MT7986 by increasing the reserved FIFO space. +Fix this on older chips by detecting the presence of small fragments and use +skb_gso_segment + skb_linearize to deal with them. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + + #include "mtk_eth_soc.h" +@@ -1607,12 +1608,28 @@ static void mtk_wake_queue(struct mtk_et + } + } + ++static bool mtk_skb_has_small_frag(struct sk_buff *skb) ++{ ++ int min_size = 16; ++ int i; ++ ++ if (skb_headlen(skb) < min_size) ++ return true; ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) ++ if (skb_frag_size(&skb_shinfo(skb)->frags[i]) < min_size) ++ return true; ++ ++ return false; ++} ++ + static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) + { + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_tx_ring *ring = ð->tx_ring; + struct net_device_stats *stats = &dev->stats; ++ struct sk_buff *segs, *next; + bool gso = false; + int tx_num; + +@@ -1641,6 +1658,18 @@ static netdev_tx_t mtk_start_xmit(struct + return NETDEV_TX_BUSY; + } + ++ if (mtk_is_netsys_v1(eth) && ++ skb_is_gso(skb) && mtk_skb_has_small_frag(skb)) { ++ segs = skb_gso_segment(skb, dev->features & ~NETIF_F_ALL_TSO); ++ if (IS_ERR(segs)) ++ goto drop; ++ ++ if (segs) { ++ consume_skb(skb); ++ skb = segs; ++ } ++ } ++ + /* TSO: fill MSS info in tcp checksum field */ + if (skb_is_gso(skb)) { + if (skb_cow_head(skb, 0)) { +@@ -1656,8 +1685,14 @@ static netdev_tx_t mtk_start_xmit(struct + } + } + +- if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0) +- goto drop; ++ skb_list_walk_safe(skb, skb, next) { ++ if ((mtk_is_netsys_v1(eth) && ++ mtk_skb_has_small_frag(skb) && skb_linearize(skb)) || ++ mtk_tx_map(skb, dev, tx_num, ring, gso) < 0) { ++ stats->tx_dropped++; ++ dev_kfree_skb_any(skb); ++ } ++ } + + if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) + netif_tx_stop_all_queues(dev); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -278,7 +278,7 @@ + #define MTK_CHK_DDONE_EN BIT(28) + #define MTK_DMAD_WR_WDONE BIT(26) + #define MTK_WCOMP_EN BIT(24) +-#define MTK_RESV_BUF (0x40 << 16) ++#define MTK_RESV_BUF (0x80 << 16) + #define MTK_MUTLI_CNT (0x4 << 12) + #define MTK_LEAKY_BUCKET_EN BIT(11) + diff --git a/target/linux/generic/pending-6.12/732-03-net-ethernet-mtk_eth_soc-optimize-dma-ring-address-i.patch b/target/linux/generic/pending-6.12/732-03-net-ethernet-mtk_eth_soc-optimize-dma-ring-address-i.patch new file mode 100644 index 00000000000..6a51dbbfb48 --- /dev/null +++ b/target/linux/generic/pending-6.12/732-03-net-ethernet-mtk_eth_soc-optimize-dma-ring-address-i.patch @@ -0,0 +1,486 @@ +From: Felix Fietkau +Date: Tue, 15 Oct 2024 12:52:56 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: optimize dma ring address/index + calculation + +Since DMA descriptor sizes are all power of 2, we can avoid costly integer +division in favor or simple shifts. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -43,6 +43,11 @@ MODULE_PARM_DESC(msg_level, "Message lev + offsetof(struct mtk_hw_stats, xdp_stats.x) / \ + sizeof(u64) } + ++#define RX_DESC_OFS(eth, i) \ ++ ((i) << (eth)->soc->rx.desc_shift) ++#define TX_DESC_OFS(eth, i) \ ++ ((i) << (eth)->soc->tx.desc_shift) ++ + static const struct mtk_reg_map mtk_reg_map = { + .tx_irq_mask = 0x1a1c, + .tx_irq_status = 0x1a18, +@@ -1160,14 +1165,14 @@ static int mtk_init_fq_dma(struct mtk_et + eth->scratch_ring = eth->sram_base; + else + eth->scratch_ring = dma_alloc_coherent(eth->dma_dev, +- cnt * soc->tx.desc_size, ++ TX_DESC_OFS(eth, cnt), + ð->phy_scratch_ring, + GFP_KERNEL); + + if (unlikely(!eth->scratch_ring)) + return -ENOMEM; + +- phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1); ++ phy_ring_tail = eth->phy_scratch_ring + TX_DESC_OFS(eth, cnt - 1); + + for (j = 0; j < DIV_ROUND_UP(soc->tx.fq_dma_size, MTK_FQ_DMA_LENGTH); j++) { + len = min_t(int, cnt - j * MTK_FQ_DMA_LENGTH, MTK_FQ_DMA_LENGTH); +@@ -1186,11 +1191,11 @@ static int mtk_init_fq_dma(struct mtk_et + for (i = 0; i < len; i++) { + struct mtk_tx_dma_v2 *txd; + +- txd = eth->scratch_ring + (j * MTK_FQ_DMA_LENGTH + i) * soc->tx.desc_size; ++ txd = eth->scratch_ring + TX_DESC_OFS(eth, j * MTK_FQ_DMA_LENGTH + i); + txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE; + if (j * MTK_FQ_DMA_LENGTH + i < cnt) + txd->txd2 = eth->phy_scratch_ring + +- (j * MTK_FQ_DMA_LENGTH + i + 1) * soc->tx.desc_size; ++ TX_DESC_OFS(eth, j * MTK_FQ_DMA_LENGTH + i + 1); + + txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); + if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA)) +@@ -1220,9 +1225,9 @@ static void *mtk_qdma_phys_to_virt(struc + } + + static struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring, +- void *txd, u32 txd_size) ++ void *txd, u32 txd_shift) + { +- int idx = (txd - ring->dma) / txd_size; ++ int idx = (txd - ring->dma) >> txd_shift; + + return &ring->buf[idx]; + } +@@ -1233,9 +1238,9 @@ static struct mtk_tx_dma *qdma_to_pdma(s + return ring->dma_pdma - (struct mtk_tx_dma *)ring->dma + dma; + } + +-static int txd_to_idx(struct mtk_tx_ring *ring, void *dma, u32 txd_size) ++static int txd_to_idx(struct mtk_tx_ring *ring, void *dma, u32 txd_shift) + { +- return (dma - ring->dma) / txd_size; ++ return (dma - ring->dma) >> txd_shift; + } + + static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, +@@ -1443,7 +1448,7 @@ static int mtk_tx_map(struct sk_buff *sk + if (itxd == ring->last_free) + return -ENOMEM; + +- itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_size); ++ itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_shift); + memset(itx_buf, 0, sizeof(*itx_buf)); + + txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size, +@@ -1497,7 +1502,7 @@ static int mtk_tx_map(struct sk_buff *sk + mtk_tx_set_dma_desc(dev, txd, &txd_info); + + tx_buf = mtk_desc_to_tx_buf(ring, txd, +- soc->tx.desc_size); ++ soc->tx.desc_shift); + if (new_desc) + memset(tx_buf, 0, sizeof(*tx_buf)); + tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; +@@ -1540,7 +1545,7 @@ static int mtk_tx_map(struct sk_buff *sk + } else { + int next_idx; + +- next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->tx.desc_size), ++ next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->tx.desc_shift), + ring->dma_size); + mtk_w32(eth, next_idx, MT7628_TX_CTX_IDX0); + } +@@ -1549,7 +1554,7 @@ static int mtk_tx_map(struct sk_buff *sk + + err_dma: + do { +- tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_size); ++ tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_shift); + + /* unmap dma */ + mtk_tx_unmap(eth, tx_buf, NULL, false); +@@ -1723,7 +1728,7 @@ static struct mtk_rx_ring *mtk_get_rx_ri + + ring = ð->rx_ring[i]; + idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); +- rxd = ring->dma + idx * eth->soc->rx.desc_size; ++ rxd = ring->dma + RX_DESC_OFS(eth, idx); + if (rxd->rxd2 & RX_DMA_DONE) { + ring->calc_idx_update = true; + return ring; +@@ -1891,7 +1896,7 @@ static int mtk_xdp_submit_frame(struct m + } + htxd = txd; + +- tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->tx.desc_size); ++ tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->tx.desc_shift); + memset(tx_buf, 0, sizeof(*tx_buf)); + htx_buf = tx_buf; + +@@ -1910,7 +1915,7 @@ static int mtk_xdp_submit_frame(struct m + goto unmap; + + tx_buf = mtk_desc_to_tx_buf(ring, txd, +- soc->tx.desc_size); ++ soc->tx.desc_shift); + memset(tx_buf, 0, sizeof(*tx_buf)); + n_desc++; + } +@@ -1948,7 +1953,7 @@ static int mtk_xdp_submit_frame(struct m + } else { + int idx; + +- idx = txd_to_idx(ring, txd, soc->tx.desc_size); ++ idx = txd_to_idx(ring, txd, soc->tx.desc_shift); + mtk_w32(eth, NEXT_DESP_IDX(idx, ring->dma_size), + MT7628_TX_CTX_IDX0); + } +@@ -1959,7 +1964,7 @@ static int mtk_xdp_submit_frame(struct m + + unmap: + while (htxd != txd) { +- tx_buf = mtk_desc_to_tx_buf(ring, htxd, soc->tx.desc_size); ++ tx_buf = mtk_desc_to_tx_buf(ring, htxd, soc->tx.desc_shift); + mtk_tx_unmap(eth, tx_buf, NULL, false); + + htxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; +@@ -2091,7 +2096,7 @@ static int mtk_poll_rx(struct napi_struc + goto rx_done; + + idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); +- rxd = ring->dma + idx * eth->soc->rx.desc_size; ++ rxd = ring->dma + RX_DESC_OFS(eth, idx); + data = ring->data[idx]; + + if (!mtk_rx_get_desc(eth, &trxd, rxd)) +@@ -2355,7 +2360,7 @@ static int mtk_poll_tx_qdma(struct mtk_e + break; + + tx_buf = mtk_desc_to_tx_buf(ring, desc, +- eth->soc->tx.desc_size); ++ eth->soc->tx.desc_shift); + if (!tx_buf->data) + break; + +@@ -2406,7 +2411,7 @@ static int mtk_poll_tx_pdma(struct mtk_e + } + mtk_tx_unmap(eth, tx_buf, &bq, true); + +- desc = ring->dma + cpu * eth->soc->tx.desc_size; ++ desc = ring->dma + TX_DESC_OFS(eth, cpu); + ring->last_free = desc; + atomic_inc(&ring->free_count); + +@@ -2524,7 +2529,7 @@ static int mtk_tx_alloc(struct mtk_eth * + { + const struct mtk_soc_data *soc = eth->soc; + struct mtk_tx_ring *ring = ð->tx_ring; +- int i, sz = soc->tx.desc_size; ++ int i, sz = TX_DESC_OFS(eth, 1); + struct mtk_tx_dma_v2 *txd; + int ring_size; + u32 ofs, val; +@@ -2571,7 +2576,7 @@ static int mtk_tx_alloc(struct mtk_eth * + * descriptors in ring->dma_pdma. + */ + if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { +- ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, ring_size * sz, ++ ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, TX_DESC_OFS(eth, ring_size), + &ring->phys_pdma, GFP_KERNEL); + if (!ring->dma_pdma) + goto no_tx_mem; +@@ -2586,7 +2591,7 @@ static int mtk_tx_alloc(struct mtk_eth * + atomic_set(&ring->free_count, ring_size - 2); + ring->next_free = ring->dma; + ring->last_free = (void *)txd; +- ring->last_free_ptr = (u32)(ring->phys + ((ring_size - 1) * sz)); ++ ring->last_free_ptr = (u32)(ring->phys + TX_DESC_OFS(eth, ring_size - 1)); + ring->thresh = MAX_SKB_FRAGS; + + /* make sure that all changes to the dma ring are flushed before we +@@ -2598,7 +2603,7 @@ static int mtk_tx_alloc(struct mtk_eth * + mtk_w32(eth, ring->phys, soc->reg_map->qdma.ctx_ptr); + mtk_w32(eth, ring->phys, soc->reg_map->qdma.dtx_ptr); + mtk_w32(eth, +- ring->phys + ((ring_size - 1) * sz), ++ ring->phys + TX_DESC_OFS(eth, ring_size - 1), + soc->reg_map->qdma.crx_ptr); + mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr); + +@@ -2647,14 +2652,14 @@ static void mtk_tx_clean(struct mtk_eth + } + if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && ring->dma) { + dma_free_coherent(eth->dma_dev, +- ring->dma_size * soc->tx.desc_size, ++ TX_DESC_OFS(eth, ring->dma_size), + ring->dma, ring->phys); + ring->dma = NULL; + } + + if (ring->dma_pdma) { + dma_free_coherent(eth->dma_dev, +- ring->dma_size * soc->tx.desc_size, ++ TX_DESC_OFS(eth, ring->dma_size), + ring->dma_pdma, ring->phys_pdma); + ring->dma_pdma = NULL; + } +@@ -2710,15 +2715,13 @@ static int mtk_rx_alloc(struct mtk_eth * + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM) || + rx_flag != MTK_RX_FLAGS_NORMAL) { + ring->dma = dma_alloc_coherent(eth->dma_dev, +- rx_dma_size * eth->soc->rx.desc_size, ++ RX_DESC_OFS(eth, rx_dma_size), + &ring->phys, GFP_KERNEL); + } else { + struct mtk_tx_ring *tx_ring = ð->tx_ring; + +- ring->dma = tx_ring->dma + tx_ring_size * +- eth->soc->tx.desc_size * (ring_no + 1); +- ring->phys = tx_ring->phys + tx_ring_size * +- eth->soc->tx.desc_size * (ring_no + 1); ++ ring->dma = tx_ring->dma + TX_DESC_OFS(eth, tx_ring_size * (ring_no + 1)); ++ ring->phys = tx_ring->phys + TX_DESC_OFS(eth, tx_ring_size * (ring_no + 1)); + } + + if (!ring->dma) +@@ -2729,7 +2732,7 @@ static int mtk_rx_alloc(struct mtk_eth * + dma_addr_t dma_addr; + void *data; + +- rxd = ring->dma + i * eth->soc->rx.desc_size; ++ rxd = ring->dma + RX_DESC_OFS(eth, i); + if (ring->page_pool) { + data = mtk_page_pool_get_buff(ring->page_pool, + &dma_addr, GFP_KERNEL); +@@ -2820,7 +2823,7 @@ static void mtk_rx_clean(struct mtk_eth + if (!ring->data[i]) + continue; + +- rxd = ring->dma + i * eth->soc->rx.desc_size; ++ rxd = ring->dma + RX_DESC_OFS(eth, i); + if (!rxd->rxd1) + continue; + +@@ -2837,7 +2840,7 @@ static void mtk_rx_clean(struct mtk_eth + + if (!in_sram && ring->dma) { + dma_free_coherent(eth->dma_dev, +- ring->dma_size * eth->soc->rx.desc_size, ++ RX_DESC_OFS(eth, ring->dma_size), + ring->dma, ring->phys); + ring->dma = NULL; + } +@@ -3208,7 +3211,7 @@ static void mtk_dma_free(struct mtk_eth + + if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && eth->scratch_ring) { + dma_free_coherent(eth->dma_dev, +- MTK_QDMA_RING_SIZE * soc->tx.desc_size, ++ TX_DESC_OFS(eth, MTK_QDMA_RING_SIZE), + eth->scratch_ring, eth->phy_scratch_ring); + eth->scratch_ring = NULL; + eth->phy_scratch_ring = 0; +@@ -5245,6 +5248,9 @@ static void mtk_remove(struct platform_d + mtk_mdio_cleanup(eth); + } + ++#define DESC_SIZE(struct_name) \ ++ .desc_shift = const_ilog2(sizeof(struct_name)) ++ + static const struct mtk_soc_data mt2701_data = { + .reg_map = &mtk_reg_map, + .caps = MT7623_CAPS | MTK_HWLRO, +@@ -5253,14 +5259,14 @@ static const struct mtk_soc_data mt2701_ + .required_pctl = true, + .version = 1, + .tx = { +- .desc_size = sizeof(struct mtk_tx_dma), ++ DESC_SIZE(struct mtk_tx_dma), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), + }, + .rx = { +- .desc_size = sizeof(struct mtk_rx_dma), ++ DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), +@@ -5281,14 +5287,14 @@ static const struct mtk_soc_data mt7621_ + .hash_offset = 2, + .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE, + .tx = { +- .desc_size = sizeof(struct mtk_tx_dma), ++ DESC_SIZE(struct mtk_tx_dma), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), + }, + .rx = { +- .desc_size = sizeof(struct mtk_rx_dma), ++ DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), +@@ -5311,14 +5317,14 @@ static const struct mtk_soc_data mt7622_ + .has_accounting = true, + .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE, + .tx = { +- .desc_size = sizeof(struct mtk_tx_dma), ++ DESC_SIZE(struct mtk_tx_dma), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), + }, + .rx = { +- .desc_size = sizeof(struct mtk_rx_dma), ++ DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), +@@ -5340,14 +5346,14 @@ static const struct mtk_soc_data mt7623_ + .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE, + .disable_pll_modes = true, + .tx = { +- .desc_size = sizeof(struct mtk_tx_dma), ++ DESC_SIZE(struct mtk_tx_dma), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), + }, + .rx = { +- .desc_size = sizeof(struct mtk_rx_dma), ++ DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), +@@ -5366,14 +5372,14 @@ static const struct mtk_soc_data mt7629_ + .has_accounting = true, + .version = 1, + .tx = { +- .desc_size = sizeof(struct mtk_tx_dma), ++ DESC_SIZE(struct mtk_tx_dma), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), + }, + .rx = { +- .desc_size = sizeof(struct mtk_rx_dma), ++ DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), +@@ -5396,14 +5402,14 @@ static const struct mtk_soc_data mt7981_ + .has_accounting = true, + .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE, + .tx = { +- .desc_size = sizeof(struct mtk_tx_dma_v2), ++ DESC_SIZE(struct mtk_tx_dma_v2), + .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, + .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), + }, + .rx = { +- .desc_size = sizeof(struct mtk_rx_dma), ++ DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID_V2, + .dma_max_len = MTK_TX_DMA_BUF_LEN, +@@ -5426,14 +5432,14 @@ static const struct mtk_soc_data mt7986_ + .has_accounting = true, + .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE, + .tx = { +- .desc_size = sizeof(struct mtk_tx_dma_v2), ++ DESC_SIZE(struct mtk_tx_dma_v2), + .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, + .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), + }, + .rx = { +- .desc_size = sizeof(struct mtk_rx_dma), ++ DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID_V2, + .dma_max_len = MTK_TX_DMA_BUF_LEN, +@@ -5456,14 +5462,14 @@ static const struct mtk_soc_data mt7988_ + .has_accounting = true, + .foe_entry_size = MTK_FOE_ENTRY_V3_SIZE, + .tx = { +- .desc_size = sizeof(struct mtk_tx_dma_v2), ++ DESC_SIZE(struct mtk_tx_dma_v2), + .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, + .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(4K), + }, + .rx = { +- .desc_size = sizeof(struct mtk_rx_dma_v2), ++ DESC_SIZE(struct mtk_rx_dma_v2), + .irq_done_mask = MTK_RX_DONE_INT_V2, + .dma_l4_valid = RX_DMA_L4_VALID_V2, + .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, +@@ -5480,13 +5486,13 @@ static const struct mtk_soc_data rt5350_ + .required_pctl = false, + .version = 1, + .tx = { +- .desc_size = sizeof(struct mtk_tx_dma), ++ DESC_SIZE(struct mtk_tx_dma), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + }, + .rx = { +- .desc_size = sizeof(struct mtk_rx_dma), ++ DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID_PDMA, + .dma_max_len = MTK_TX_DMA_BUF_LEN, +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1160,7 +1160,7 @@ struct mtk_reg_map { + * @foe_entry_size Foe table entry size. + * @has_accounting Bool indicating support for accounting of + * offloaded flows. +- * @desc_size Tx/Rx DMA descriptor size. ++ * @desc_shift Tx/Rx DMA descriptor size (in power-of-2). + * @irq_done_mask Rx irq done register mask. + * @dma_l4_valid Rx DMA valid register mask. + * @dma_max_len Max DMA tx/rx buffer length. +@@ -1181,14 +1181,14 @@ struct mtk_soc_data { + bool has_accounting; + bool disable_pll_modes; + struct { +- u32 desc_size; ++ u32 desc_shift; + u32 dma_max_len; + u32 dma_len_offset; + u32 dma_size; + u32 fq_dma_size; + } tx; + struct { +- u32 desc_size; ++ u32 desc_shift; + u32 irq_done_mask; + u32 dma_l4_valid; + u32 dma_max_len; diff --git a/target/linux/generic/pending-6.12/732-04-net-ethernet-mtk_eth_soc-shrink-struct-mtk_tx_buf.patch b/target/linux/generic/pending-6.12/732-04-net-ethernet-mtk_eth_soc-shrink-struct-mtk_tx_buf.patch new file mode 100644 index 00000000000..a2dd04b3788 --- /dev/null +++ b/target/linux/generic/pending-6.12/732-04-net-ethernet-mtk_eth_soc-shrink-struct-mtk_tx_buf.patch @@ -0,0 +1,124 @@ +From: Felix Fietkau +Date: Mon, 14 Jul 2025 10:52:59 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: shrink struct mtk_tx_buf + +There is no need to track the difference between dma_map_page +and dma_map_single, since they're unmapped in exactly the same way. +Also reorder fields in order to avoid padding. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1246,32 +1246,19 @@ static int txd_to_idx(struct mtk_tx_ring + static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, + struct xdp_frame_bulk *bq, bool napi) + { +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { +- if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) { +- dma_unmap_single(eth->dma_dev, +- dma_unmap_addr(tx_buf, dma_addr0), +- dma_unmap_len(tx_buf, dma_len0), +- DMA_TO_DEVICE); +- } else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) { +- dma_unmap_page(eth->dma_dev, +- dma_unmap_addr(tx_buf, dma_addr0), +- dma_unmap_len(tx_buf, dma_len0), +- DMA_TO_DEVICE); +- } +- } else { +- if (dma_unmap_len(tx_buf, dma_len0)) { +- dma_unmap_page(eth->dma_dev, +- dma_unmap_addr(tx_buf, dma_addr0), +- dma_unmap_len(tx_buf, dma_len0), +- DMA_TO_DEVICE); +- } ++ if (dma_unmap_len(tx_buf, dma_len0)) { ++ dma_unmap_page(eth->dma_dev, ++ dma_unmap_addr(tx_buf, dma_addr0), ++ dma_unmap_len(tx_buf, dma_len0), ++ DMA_TO_DEVICE); ++ } + +- if (dma_unmap_len(tx_buf, dma_len1)) { +- dma_unmap_page(eth->dma_dev, +- dma_unmap_addr(tx_buf, dma_addr1), +- dma_unmap_len(tx_buf, dma_len1), +- DMA_TO_DEVICE); +- } ++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA) && ++ dma_unmap_len(tx_buf, dma_len1)) { ++ dma_unmap_page(eth->dma_dev, ++ dma_unmap_addr(tx_buf, dma_addr1), ++ dma_unmap_len(tx_buf, dma_len1), ++ DMA_TO_DEVICE); + } + + if (tx_buf->data && tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { +@@ -1293,7 +1280,6 @@ static void mtk_tx_unmap(struct mtk_eth + xdp_return_frame(xdpf); + } + } +- tx_buf->flags = 0; + tx_buf->data = NULL; + } + +@@ -1458,7 +1444,6 @@ static int mtk_tx_map(struct sk_buff *sk + + mtk_tx_set_dma_desc(dev, itxd, &txd_info); + +- itx_buf->flags |= MTK_TX_FLAGS_SINGLE0; + itx_buf->mac_id = mac->id; + setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size, + k++); +@@ -1506,7 +1491,6 @@ static int mtk_tx_map(struct sk_buff *sk + if (new_desc) + memset(tx_buf, 0, sizeof(*tx_buf)); + tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; +- tx_buf->flags |= MTK_TX_FLAGS_PAGE0; + tx_buf->mac_id = mac->id; + + setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, +@@ -1839,8 +1823,6 @@ static int mtk_xdp_frame_map(struct mtk_ + txd_info->size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(eth->dma_dev, txd_info->addr))) + return -ENOMEM; +- +- tx_buf->flags |= MTK_TX_FLAGS_SINGLE0; + } else { + struct page *page = virt_to_head_page(data); + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -701,14 +701,6 @@ struct mtk_hw_stats { + struct u64_stats_sync syncp; + }; + +-enum mtk_tx_flags { +- /* PDMA descriptor can point at 1-2 segments. This enum allows us to +- * track how memory was allocated so that it can be freed properly. +- */ +- MTK_TX_FLAGS_SINGLE0 = 0x01, +- MTK_TX_FLAGS_PAGE0 = 0x02, +-}; +- + /* This enum allows us to identify how the clock is defined on the array of the + * clock in the order + */ +@@ -881,13 +873,12 @@ enum mtk_tx_buf_type { + */ + struct mtk_tx_buf { + enum mtk_tx_buf_type type; ++ u16 mac_id; + void *data; + +- u16 mac_id; +- u16 flags; + DEFINE_DMA_UNMAP_ADDR(dma_addr0); +- DEFINE_DMA_UNMAP_LEN(dma_len0); + DEFINE_DMA_UNMAP_ADDR(dma_addr1); ++ DEFINE_DMA_UNMAP_LEN(dma_len0); + DEFINE_DMA_UNMAP_LEN(dma_len1); + }; + diff --git a/target/linux/generic/pending-6.12/732-05-net-ethernet-mtk_eth_soc-add-support-for-sending-fra.patch b/target/linux/generic/pending-6.12/732-05-net-ethernet-mtk_eth_soc-add-support-for-sending-fra.patch new file mode 100644 index 00000000000..8dad080fa62 --- /dev/null +++ b/target/linux/generic/pending-6.12/732-05-net-ethernet-mtk_eth_soc-add-support-for-sending-fra.patch @@ -0,0 +1,519 @@ +From: Felix Fietkau +Date: Mon, 14 Jul 2025 10:41:27 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for sending + fraglist GSO packets + +When primarily forwarding traffic, TCP fraglist GRO can be noticeably more +efficient than regular TCP GRO. In order to avoid the overhead of +unnecessary segmentation on ethernet tx, add support for sending fraglist +GRO packets. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -18,6 +18,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -27,6 +29,7 @@ + #include + #include + #include ++#include + #include + + #include "mtk_eth_soc.h" +@@ -1404,119 +1407,251 @@ static void mtk_tx_set_dma_desc(struct n + mtk_tx_set_dma_desc_v1(dev, txd, info); + } + ++struct mtk_tx_map_state { ++ struct mtk_tx_dma *txd, *txd_pdma; ++ struct mtk_tx_buf *tx_buf; ++ int nbuf; ++ int ndesc; ++}; ++ ++static void ++mtk_tx_map_set_txd(struct mtk_tx_map_state *state, struct mtk_tx_ring *ring, ++ const struct mtk_soc_data *soc, struct mtk_tx_dma *txd) ++{ ++ state->txd = txd; ++ state->txd_pdma = qdma_to_pdma(ring, txd); ++ state->tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->tx.desc_shift); ++ memset(state->tx_buf, 0, sizeof(*state->tx_buf)); ++} ++ ++static int ++mtk_tx_map_info(struct mtk_eth *eth, struct mtk_tx_ring *ring, ++ struct net_device *dev, struct mtk_tx_map_state *state, ++ struct mtk_tx_dma_desc_info *txd_info) ++{ ++ const struct mtk_soc_data *soc = eth->soc; ++ struct mtk_tx_buf *tx_buf = state->tx_buf; ++ struct mtk_tx_dma *txd = state->txd; ++ struct mtk_mac *mac = netdev_priv(dev); ++ ++ if (state->nbuf && ++ (MTK_HAS_CAPS(soc->caps, MTK_QDMA) || (state->nbuf & 1) == 0)) { ++ txd = mtk_qdma_phys_to_virt(ring, txd->txd2); ++ if (txd == ring->last_free) ++ return -1; ++ ++ mtk_tx_map_set_txd(state, ring, soc, txd); ++ state->ndesc++; ++ } ++ ++ mtk_tx_set_dma_desc(dev, txd, txd_info); ++ tx_buf = state->tx_buf; ++ tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; ++ tx_buf->mac_id = mac->id; ++ ++ setup_tx_buf(eth, tx_buf, state->txd_pdma, txd_info->addr, ++ txd_info->size, state->nbuf++); ++ return 0; ++} ++ ++static void ++mtk_tx_update_ipaddr(struct sk_buff *skb, ++ struct iphdr *iph, struct tcphdr *th, ++ __be32 *old_ip, __be32 new_ip) ++{ ++ if (*old_ip == new_ip) ++ return; ++ ++ inet_proto_csum_replace4(&th->check, skb, *old_ip, new_ip, true); ++ csum_replace4(&iph->check, *old_ip, new_ip); ++ *old_ip = new_ip; ++} ++ ++static void ++mtk_tx_update_ip6addr(struct sk_buff *skb, struct ipv6hdr *iph, ++ struct tcphdr *th, struct in6_addr *old_ip, ++ const struct in6_addr *new_ip) ++{ ++ if (ipv6_addr_equal(old_ip, new_ip)) ++ return; ++ ++ inet_proto_csum_replace16(&th->check, skb, old_ip->s6_addr32, ++ new_ip->s6_addr32, true); ++ *old_ip = *new_ip; ++} ++ ++static void ++mtk_tx_update_port(struct sk_buff *skb, struct tcphdr *th, ++ __be16 *old_port, __be16 new_port) ++{ ++ if (*old_port == new_port) ++ return; ++ ++ inet_proto_csum_replace2(&th->check, skb, *old_port, new_port, false); ++ *old_port = new_port; ++} ++ + static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, +- int tx_num, struct mtk_tx_ring *ring, bool gso) ++ int tx_num, struct mtk_tx_ring *ring, bool gso, ++ unsigned int header_len) + { +- struct mtk_tx_dma_desc_info txd_info = { +- .size = skb_headlen(skb), +- .gso = gso, +- .csum = skb->ip_summed == CHECKSUM_PARTIAL, +- .vlan = skb_vlan_tag_present(skb), +- .qid = skb_get_queue_mapping(skb), +- .vlan_tci = skb_vlan_tag_get(skb), +- .first = true, +- .last = !skb_is_nonlinear(skb), ++ struct mtk_tx_dma_desc_info txd_info; ++ struct mtk_tx_map_state state = { ++ .ndesc = 1, + }; + struct netdev_queue *txq; + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + const struct mtk_soc_data *soc = eth->soc; +- struct mtk_tx_dma *itxd, *txd; +- struct mtk_tx_dma *itxd_pdma, *txd_pdma; +- struct mtk_tx_buf *itx_buf, *tx_buf; +- int i, n_desc = 1; ++ struct mtk_tx_dma *itxd; ++ struct sk_buff *cur_skb, *next_skb; + int queue = skb_get_queue_mapping(skb); +- int k = 0; ++ int i, frag_size = skb_headlen(skb); ++ bool gso_v4, gso_fraglist; ++ int offset = 0; + + txq = netdev_get_tx_queue(dev, queue); + itxd = ring->next_free; +- itxd_pdma = qdma_to_pdma(ring, itxd); + if (itxd == ring->last_free) + return -ENOMEM; + +- itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_shift); +- memset(itx_buf, 0, sizeof(*itx_buf)); ++ cur_skb = skb; ++ next_skb = skb_shinfo(skb)->frag_list; ++ mtk_tx_map_set_txd(&state, ring, soc, itxd); ++ gso_v4 = skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4; ++ gso_fraglist = skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST; + +- txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size, +- DMA_TO_DEVICE); +- if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr))) +- return -ENOMEM; ++next: ++ txd_info = (struct mtk_tx_dma_desc_info){ ++ .gso = gso, ++ .qid = queue, ++ .csum = cur_skb->ip_summed == CHECKSUM_PARTIAL || gso, ++ .vlan = skb_vlan_tag_present(skb), ++ .vlan_tci = skb_vlan_tag_get(skb), ++ .first = true, ++ }; + +- mtk_tx_set_dma_desc(dev, itxd, &txd_info); ++ if (cur_skb != skb) { ++ struct tcphdr *th, *th2; + +- itx_buf->mac_id = mac->id; +- setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size, +- k++); +- +- /* TX SG offload */ +- txd = itxd; +- txd_pdma = qdma_to_pdma(ring, txd); +- +- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { +- skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; +- unsigned int offset = 0; +- int frag_size = skb_frag_size(frag); ++ if (skb_cow_head(cur_skb, header_len)) ++ goto err_dma; + +- while (frag_size) { +- bool new_desc = true; ++ memcpy(cur_skb->data - header_len, skb->data, ++ skb_network_offset(skb)); + +- if (MTK_HAS_CAPS(soc->caps, MTK_QDMA) || +- (i & 0x1)) { +- txd = mtk_qdma_phys_to_virt(ring, txd->txd2); +- txd_pdma = qdma_to_pdma(ring, txd); +- if (txd == ring->last_free) +- goto err_dma; ++ th = tcp_hdr(cur_skb); ++ th2 = tcp_hdr(skb); ++ if (gso_v4) { ++ struct iphdr *iph = ip_hdr(cur_skb); ++ struct iphdr *iph2 = ip_hdr(skb); ++ ++ mtk_tx_update_ipaddr(skb, iph, th, &iph->saddr, ++ iph2->saddr); ++ mtk_tx_update_ipaddr(skb, iph, th, &iph->daddr, ++ iph2->daddr); ++ } else { ++ struct ipv6hdr *iph = ipv6_hdr(cur_skb); ++ struct ipv6hdr *iph2 = ipv6_hdr(skb); + +- n_desc++; +- } else { +- new_desc = false; +- } ++ mtk_tx_update_ip6addr(skb, iph, th, &iph->saddr, ++ &iph2->saddr); ++ mtk_tx_update_ip6addr(skb, iph, th, &iph->daddr, ++ &iph2->daddr); ++ } ++ ++ mtk_tx_update_port(skb, th, &th->source, th2->source); ++ mtk_tx_update_port(skb, th, &th->dest, th2->dest); ++ ++ offset = -header_len; ++ frag_size += header_len; ++ } else if (next_skb && gso_fraglist) { ++ unsigned int ip_len = skb_pagelen(skb) - skb_network_offset(skb); ++ if (gso_v4) { ++ struct iphdr *iph = ip_hdr(cur_skb); ++ __be16 ip_len_val = cpu_to_be16(ip_len); + +- memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); ++ csum_replace2(&iph->check, iph->tot_len, ip_len_val); ++ iph->tot_len = ip_len_val; ++ } else { ++ struct ipv6hdr *iph = ipv6_hdr(cur_skb); ++ __be16 ip_len_val = cpu_to_be16(ip_len - sizeof(*iph)); ++ ++ iph->payload_len = ip_len_val; ++ } ++ } ++ ++next_frag: ++ while (frag_size) { ++ txd_info.size = min_t(unsigned int, frag_size, ++ soc->tx.dma_max_len); ++ txd_info.addr = dma_map_single(eth->dma_dev, cur_skb->data + offset, ++ txd_info.size, DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr))) ++ goto err_dma; ++ ++ frag_size -= txd_info.size; ++ offset += txd_info.size; ++ txd_info.last = !frag_size && !skb_shinfo(cur_skb)->nr_frags && ++ (gso_fraglist || !next_skb); ++ if (mtk_tx_map_info(eth, ring, dev, &state, &txd_info) < 0) ++ goto err_dma; ++ } ++ ++ for (i = 0; i < skb_shinfo(cur_skb)->nr_frags; i++) { ++ skb_frag_t *frag = &skb_shinfo(cur_skb)->frags[i]; ++ ++ frag_size = skb_frag_size(frag); ++ memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); ++ txd_info.qid = queue; ++ offset = 0; ++ while (frag_size) { + txd_info.size = min_t(unsigned int, frag_size, + soc->tx.dma_max_len); +- txd_info.qid = queue; +- txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 && +- !(frag_size - txd_info.size); +- txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag, +- offset, txd_info.size, +- DMA_TO_DEVICE); ++ txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag, offset, ++ txd_info.size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr))) + goto err_dma; + +- mtk_tx_set_dma_desc(dev, txd, &txd_info); +- +- tx_buf = mtk_desc_to_tx_buf(ring, txd, +- soc->tx.desc_shift); +- if (new_desc) +- memset(tx_buf, 0, sizeof(*tx_buf)); +- tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; +- tx_buf->mac_id = mac->id; +- +- setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, +- txd_info.size, k++); +- + frag_size -= txd_info.size; + offset += txd_info.size; ++ txd_info.last = i == skb_shinfo(cur_skb)->nr_frags - 1 && ++ !frag_size && (gso_fraglist || !next_skb); ++ if (mtk_tx_map_info(eth, ring, dev, &state, &txd_info) < 0) ++ goto err_dma; + } + } + +- /* store skb to cleanup */ +- itx_buf->type = MTK_TYPE_SKB; +- itx_buf->data = skb; ++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA) && ++ (gso_fraglist || !next_skb)) { ++ if (state.nbuf & 0x1) { ++ state.txd_pdma->txd2 |= TX_DMA_LS0; ++ state.nbuf++; ++ } else { ++ state.txd_pdma->txd2 |= TX_DMA_LS1; ++ } ++ } ++ ++ if (next_skb) { ++ cur_skb = next_skb; ++ next_skb = cur_skb->next; ++ offset = 0; ++ frag_size = skb_headlen(cur_skb); ++ if (!gso_fraglist) ++ goto next_frag; + +- if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { +- if (k & 0x1) +- txd_pdma->txd2 |= TX_DMA_LS0; +- else +- txd_pdma->txd2 |= TX_DMA_LS1; ++ goto next; + } + ++ /* store skb to cleanup */ ++ state.tx_buf->type = MTK_TYPE_SKB; ++ state.tx_buf->data = skb; ++ + netdev_tx_sent_queue(txq, skb->len); + skb_tx_timestamp(skb); + +- ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2); +- atomic_sub(n_desc, &ring->free_count); ++ ring->next_free = mtk_qdma_phys_to_virt(ring, state.txd->txd2); ++ atomic_sub(state.ndesc, &ring->free_count); + + /* make sure that all changes to the dma ring are flushed before we + * continue +@@ -1525,11 +1660,11 @@ static int mtk_tx_map(struct sk_buff *sk + + if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { + if (netif_xmit_stopped(txq) || !netdev_xmit_more()) +- mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr); ++ mtk_w32(eth, state.txd->txd2, soc->reg_map->qdma.ctx_ptr); + } else { + int next_idx; + +- next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->tx.desc_shift), ++ next_idx = NEXT_DESP_IDX(txd_to_idx(ring, state.txd, soc->tx.desc_shift), + ring->dma_size); + mtk_w32(eth, next_idx, MT7628_TX_CTX_IDX0); + } +@@ -1538,18 +1673,20 @@ static int mtk_tx_map(struct sk_buff *sk + + err_dma: + do { +- tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_shift); ++ struct mtk_tx_dma *itxd_pdma = qdma_to_pdma(ring, itxd); ++ struct mtk_tx_buf *itx_buf; ++ ++ itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_shift); + + /* unmap dma */ +- mtk_tx_unmap(eth, tx_buf, NULL, false); ++ mtk_tx_unmap(eth, itx_buf, NULL, false); + + itxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; + if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) + itxd_pdma->txd2 = TX_DMA_DESP2_DEF; + + itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2); +- itxd_pdma = qdma_to_pdma(ring, itxd); +- } while (itxd != txd); ++ } while (itxd != state.txd); + + return -ENOMEM; + } +@@ -1569,6 +1706,9 @@ static int mtk_cal_txd_req(struct mtk_et + nfrags += skb_shinfo(skb)->nr_frags; + } + ++ for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next) ++ nfrags += mtk_cal_txd_req(eth, skb) + 1; ++ + return nfrags; + } + +@@ -1609,9 +1749,29 @@ static bool mtk_skb_has_small_frag(struc + if (skb_frag_size(&skb_shinfo(skb)->frags[i]) < min_size) + return true; + ++ for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next) ++ if (mtk_skb_has_small_frag(skb)) ++ return true; ++ + return false; + } + ++static bool mtk_skb_valid_gso(struct mtk_eth *eth, struct sk_buff *skb, ++ unsigned int header_len) ++{ ++ if (mtk_is_netsys_v1(eth) && mtk_skb_has_small_frag(skb)) ++ return false; ++ ++ if (!(skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)) ++ return true; ++ ++ if (skb->encapsulation) ++ return false; ++ ++ return skb_pagelen(skb) - header_len == skb_shinfo(skb)->gso_size && ++ skb_headlen(skb) > header_len; ++} ++ + static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) + { + struct mtk_mac *mac = netdev_priv(dev); +@@ -1619,6 +1779,7 @@ static netdev_tx_t mtk_start_xmit(struct + struct mtk_tx_ring *ring = ð->tx_ring; + struct net_device_stats *stats = &dev->stats; + struct sk_buff *segs, *next; ++ unsigned int header_len = 0; + bool gso = false; + int tx_num; + +@@ -1647,37 +1808,42 @@ static netdev_tx_t mtk_start_xmit(struct + return NETDEV_TX_BUSY; + } + +- if (mtk_is_netsys_v1(eth) && +- skb_is_gso(skb) && mtk_skb_has_small_frag(skb)) { +- segs = skb_gso_segment(skb, dev->features & ~NETIF_F_ALL_TSO); +- if (IS_ERR(segs)) +- goto drop; +- +- if (segs) { +- consume_skb(skb); +- skb = segs; +- } +- } +- +- /* TSO: fill MSS info in tcp checksum field */ + if (skb_is_gso(skb)) { +- if (skb_cow_head(skb, 0)) { +- netif_warn(eth, tx_err, dev, +- "GSO expand head fail.\n"); +- goto drop; ++ header_len = skb_tcp_all_headers(skb); ++ if (!mtk_skb_valid_gso(eth, skb, header_len)) { ++ segs = skb_gso_segment(skb, dev->features & ~NETIF_F_ALL_TSO); ++ if (IS_ERR(segs)) ++ goto drop; ++ ++ if (segs) { ++ consume_skb(skb); ++ skb = segs; ++ } ++ goto send; + } + ++ if ((skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)) ++ goto send; ++ + if (skb_shinfo(skb)->gso_type & + (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) { ++ /* TSO: fill MSS info in tcp checksum field */ + gso = true; ++ if (skb_cow_head(skb, 0)) { ++ netif_warn(eth, tx_err, dev, ++ "GSO expand head fail.\n"); ++ goto drop; ++ } ++ + tcp_hdr(skb)->check = htons(skb_shinfo(skb)->gso_size); + } + } + ++send: + skb_list_walk_safe(skb, skb, next) { + if ((mtk_is_netsys_v1(eth) && + mtk_skb_has_small_frag(skb) && skb_linearize(skb)) || +- mtk_tx_map(skb, dev, tx_num, ring, gso) < 0) { ++ mtk_tx_map(skb, dev, tx_num, ring, gso, header_len) < 0) { + stats->tx_dropped++; + dev_kfree_skb_any(skb); + } +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -51,6 +51,8 @@ + NETIF_F_HW_VLAN_CTAG_TX | \ + NETIF_F_SG | NETIF_F_TSO | \ + NETIF_F_TSO6 | \ ++ NETIF_F_FRAGLIST | \ ++ NETIF_F_GSO_FRAGLIST | \ + NETIF_F_IPV6_CSUM |\ + NETIF_F_HW_TC) + #define MTK_HW_FEATURES_MT7628 (NETIF_F_SG | NETIF_F_RXCSUM) diff --git a/target/linux/generic/pending-6.12/733-01-net-ethernet-mtk_eth_soc-use-napi_build_skb.patch b/target/linux/generic/pending-6.12/733-01-net-ethernet-mtk_eth_soc-use-napi_build_skb.patch new file mode 100644 index 00000000000..f87315ac957 --- /dev/null +++ b/target/linux/generic/pending-6.12/733-01-net-ethernet-mtk_eth_soc-use-napi_build_skb.patch @@ -0,0 +1,30 @@ +From: Felix Fietkau +Date: Mon, 20 May 2024 14:29:58 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: use napi_build_skb() + +The napi_build_skb() can reuse the skb in skb cache per CPU or +can allocate skbs in bulk, which helps improve the performance. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -2312,7 +2312,7 @@ static int mtk_poll_rx(struct napi_struc + if (ret != XDP_PASS) + goto skip_rx; + +- skb = build_skb(data, PAGE_SIZE); ++ skb = napi_build_skb(data, PAGE_SIZE); + if (unlikely(!skb)) { + page_pool_put_full_page(ring->page_pool, + page, true); +@@ -2350,7 +2350,7 @@ static int mtk_poll_rx(struct napi_struc + dma_unmap_single(eth->dma_dev, ((u64)trxd.rxd1 | addr64), + ring->buf_size, DMA_FROM_DEVICE); + +- skb = build_skb(data, ring->frag_size); ++ skb = napi_build_skb(data, ring->frag_size); + if (unlikely(!skb)) { + netdev->stats.rx_dropped++; + skb_free_frag(data); diff --git a/target/linux/generic/pending-6.12/734-net-ethernet-mediatek-enlarge-DMA-reserve-buffer.patch b/target/linux/generic/pending-6.12/734-net-ethernet-mediatek-enlarge-DMA-reserve-buffer.patch new file mode 100644 index 00000000000..8d0f0b98025 --- /dev/null +++ b/target/linux/generic/pending-6.12/734-net-ethernet-mediatek-enlarge-DMA-reserve-buffer.patch @@ -0,0 +1,44 @@ +From: Chad Monroe +Date: Mon, 16 Sep 2024 19:29:03 -0700 +Subject: [PATCH] net: ethernet: mediatek: increase QDMA RESV_BUF size + +Increase QDMA RESV_BUF from 2K to 3K for netsys v2 to match Mediatek SDK[1]. +This helps reduce the possibility of Ethernet transmit timeouts. + +[1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/19d8456c3051e5f6dabf42fa770916a2126ea4bf + +Signed-off-by: Chad Monroe +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 6 ++++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 + + 2 files changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -282,6 +282,7 @@ + #define MTK_WCOMP_EN BIT(24) + #define MTK_RESV_BUF (0x80 << 16) + #define MTK_MUTLI_CNT (0x4 << 12) ++#define MTK_RESV_BUF_MASK (0xff << 16) + #define MTK_LEAKY_BUCKET_EN BIT(11) + + /* QDMA Flow Control Register */ +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3483,12 +3483,14 @@ static int mtk_start_dma(struct mtk_eth + MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO | + MTK_RX_2B_OFFSET | MTK_TX_WB_DDONE; + +- if (mtk_is_netsys_v2_or_greater(eth)) ++ if (mtk_is_netsys_v2_or_greater(eth)) { ++ val &= ~MTK_RESV_BUF_MASK; + val |= MTK_MUTLI_CNT | MTK_RESV_BUF | + MTK_WCOMP_EN | MTK_DMAD_WR_WDONE | + MTK_CHK_DDONE_EN; +- else ++ } else { + val |= MTK_RX_BT_32DWORDS; ++ } + mtk_w32(eth, val, reg_map->qdma.glo_cfg); + + mtk_w32(eth, diff --git a/target/linux/generic/pending-6.12/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch b/target/linux/generic/pending-6.12/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch new file mode 100644 index 00000000000..c50348382a6 --- /dev/null +++ b/target/linux/generic/pending-6.12/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch @@ -0,0 +1,334 @@ +From: Felix Fietkau +Date: Thu, 23 Mar 2023 10:24:11 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: improve keeping track of + offloaded flows + +Unify tracking of L2 and L3 flows. Use the generic list field in struct +mtk_foe_entry for tracking L2 subflows. Preparation for improving +flow accounting support. + +Signed-off-by: Felix Fietkau +--- + drivers/net/ethernet/mediatek/mtk_ppe.c | 162 ++++++++++++------------ + drivers/net/ethernet/mediatek/mtk_ppe.h | 15 +-- + 2 files changed, 86 insertions(+), 91 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -479,42 +479,43 @@ int mtk_foe_entry_set_queue(struct mtk_e + return 0; + } + ++static int ++mtk_flow_entry_match_len(struct mtk_eth *eth, struct mtk_foe_entry *entry) ++{ ++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1); ++ ++ if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) ++ return offsetof(struct mtk_foe_entry, ipv6._rsv); ++ else ++ return offsetof(struct mtk_foe_entry, ipv4.ib2); ++} ++ + static bool + mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry, +- struct mtk_foe_entry *data) ++ struct mtk_foe_entry *data, int len) + { +- int type, len; +- + if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP) + return false; + +- type = mtk_get_ib1_pkt_type(eth, entry->data.ib1); +- if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) +- len = offsetof(struct mtk_foe_entry, ipv6._rsv); +- else +- len = offsetof(struct mtk_foe_entry, ipv4.ib2); +- + return !memcmp(&entry->data.data, &data->data, len - 4); + } + + static void +-__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ bool set_state) + { +- struct hlist_head *head; + struct hlist_node *tmp; + + if (entry->type == MTK_FLOW_TYPE_L2) { + rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node, + mtk_flow_l2_ht_params); + +- head = &entry->l2_flows; +- hlist_for_each_entry_safe(entry, tmp, head, l2_data.list) +- __mtk_foe_entry_clear(ppe, entry); ++ hlist_for_each_entry_safe(entry, tmp, &entry->l2_flows, l2_list) ++ __mtk_foe_entry_clear(ppe, entry, set_state); + return; + } + +- hlist_del_init(&entry->list); +- if (entry->hash != 0xffff) { ++ if (entry->hash != 0xffff && set_state) { + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); + + hwe->ib1 &= ~MTK_FOE_IB1_STATE; +@@ -535,7 +536,8 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp + if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW) + return; + +- hlist_del_init(&entry->l2_data.list); ++ hlist_del_init(&entry->l2_list); ++ hlist_del_init(&entry->list); + kfree(entry); + } + +@@ -551,66 +553,55 @@ static int __mtk_foe_entry_idle_time(str + return now - timestamp; + } + ++static bool ++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ struct mtk_foe_entry foe = {}; ++ struct mtk_foe_entry *hwe; ++ u16 hash = entry->hash; ++ int len; ++ ++ if (hash == 0xffff) ++ return false; ++ ++ hwe = mtk_foe_get_entry(ppe, hash); ++ len = mtk_flow_entry_match_len(ppe->eth, &entry->data); ++ memcpy(&foe, hwe, len); ++ ++ if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || ++ FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) ++ return false; ++ ++ entry->data.ib1 = foe.ib1; ++ ++ return true; ++} ++ + static void + mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); + struct mtk_flow_entry *cur; +- struct mtk_foe_entry *hwe; + struct hlist_node *tmp; + int idle; + + idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); +- hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) { ++ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) { + int cur_idle; +- u32 ib1; +- +- hwe = mtk_foe_get_entry(ppe, cur->hash); +- ib1 = READ_ONCE(hwe->ib1); + +- if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) { +- cur->hash = 0xffff; +- __mtk_foe_entry_clear(ppe, cur); ++ if (!mtk_flow_entry_update(ppe, cur)) { ++ __mtk_foe_entry_clear(ppe, entry, false); + continue; + } + +- cur_idle = __mtk_foe_entry_idle_time(ppe, ib1); ++ cur_idle = __mtk_foe_entry_idle_time(ppe, cur->data.ib1); + if (cur_idle >= idle) + continue; + + idle = cur_idle; + entry->data.ib1 &= ~ib1_ts_mask; +- entry->data.ib1 |= ib1 & ib1_ts_mask; +- } +-} +- +-static void +-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) +-{ +- struct mtk_foe_entry foe = {}; +- struct mtk_foe_entry *hwe; +- +- spin_lock_bh(&ppe_lock); +- +- if (entry->type == MTK_FLOW_TYPE_L2) { +- mtk_flow_entry_update_l2(ppe, entry); +- goto out; ++ entry->data.ib1 |= cur->data.ib1 & ib1_ts_mask; + } +- +- if (entry->hash == 0xffff) +- goto out; +- +- hwe = mtk_foe_get_entry(ppe, entry->hash); +- memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size); +- if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) { +- entry->hash = 0xffff; +- goto out; +- } +- +- entry->data.ib1 = foe.ib1; +- +-out: +- spin_unlock_bh(&ppe_lock); + } + + static void +@@ -653,7 +644,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p + void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + spin_lock_bh(&ppe_lock); +- __mtk_foe_entry_clear(ppe, entry); ++ __mtk_foe_entry_clear(ppe, entry, true); ++ hlist_del_init(&entry->list); + spin_unlock_bh(&ppe_lock); + } + +@@ -700,8 +692,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ + { + const struct mtk_soc_data *soc = ppe->eth->soc; + struct mtk_flow_entry *flow_info; +- struct mtk_foe_entry foe = {}, *hwe; + struct mtk_foe_mac_info *l2; ++ struct mtk_foe_entry *hwe; + u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP; + int type; + +@@ -709,30 +701,30 @@ mtk_foe_entry_commit_subflow(struct mtk_ + if (!flow_info) + return; + +- flow_info->l2_data.base_flow = entry; + flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW; + flow_info->hash = hash; + hlist_add_head(&flow_info->list, + &ppe->foe_flow[hash / soc->hash_offset]); +- hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); ++ hlist_add_head(&flow_info->l2_list, &entry->l2_flows); + + hwe = mtk_foe_get_entry(ppe, hash); +- memcpy(&foe, hwe, soc->foe_entry_size); +- foe.ib1 &= ib1_mask; +- foe.ib1 |= entry->data.ib1 & ~ib1_mask; ++ memcpy(&flow_info->data, hwe, soc->foe_entry_size); ++ flow_info->data.ib1 &= ib1_mask; ++ flow_info->data.ib1 |= entry->data.ib1 & ~ib1_mask; + +- l2 = mtk_foe_entry_l2(ppe->eth, &foe); ++ l2 = mtk_foe_entry_l2(ppe->eth, &flow_info->data); + memcpy(l2, &entry->data.bridge.l2, sizeof(*l2)); + +- type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1); ++ type = mtk_get_ib1_pkt_type(ppe->eth, flow_info->data.ib1); + if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT) +- memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new)); ++ memcpy(&flow_info->data.ipv4.new, &flow_info->data.ipv4.orig, ++ sizeof(flow_info->data.ipv4.new)); + else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP) + l2->etype = ETH_P_IPV6; + +- *mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2; ++ *mtk_foe_entry_ib2(ppe->eth, &flow_info->data) = entry->data.bridge.ib2; + +- __mtk_foe_entry_commit(ppe, &foe, hash); ++ __mtk_foe_entry_commit(ppe, &flow_info->data, hash); + } + + void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) +@@ -742,9 +734,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash); + struct mtk_flow_entry *entry; + struct mtk_foe_bridge key = {}; ++ struct mtk_foe_entry foe = {}; + struct hlist_node *n; + struct ethhdr *eh; + bool found = false; ++ int entry_len; + u8 *tag; + + spin_lock_bh(&ppe_lock); +@@ -752,20 +746,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe + if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND) + goto out; + +- hlist_for_each_entry_safe(entry, n, head, list) { +- if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) { +- if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == +- MTK_FOE_STATE_BIND)) +- continue; +- +- entry->hash = 0xffff; +- __mtk_foe_entry_clear(ppe, entry); +- continue; +- } ++ entry_len = mtk_flow_entry_match_len(ppe->eth, hwe); ++ memcpy(&foe, hwe, entry_len); + +- if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) { ++ hlist_for_each_entry_safe(entry, n, head, list) { ++ if (found || ++ !mtk_flow_entry_match(ppe->eth, entry, &foe, entry_len)) { + if (entry->hash != 0xffff) +- entry->hash = 0xffff; ++ __mtk_foe_entry_clear(ppe, entry, false); + continue; + } + +@@ -816,9 +804,17 @@ out: + + int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { +- mtk_flow_entry_update(ppe, entry); ++ int idle; ++ ++ spin_lock_bh(&ppe_lock); ++ if (entry->type == MTK_FLOW_TYPE_L2) ++ mtk_flow_entry_update_l2(ppe, entry); ++ else ++ mtk_flow_entry_update(ppe, entry); ++ idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); ++ spin_unlock_bh(&ppe_lock); + +- return __mtk_foe_entry_idle_time(ppe, entry->data.ib1); ++ return idle; + } + + int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -286,7 +286,12 @@ enum { + + struct mtk_flow_entry { + union { +- struct hlist_node list; ++ /* regular flows + L2 subflows */ ++ struct { ++ struct hlist_node list; ++ struct hlist_node l2_list; ++ }; ++ /* L2 flows */ + struct { + struct rhash_head l2_node; + struct hlist_head l2_flows; +@@ -296,13 +301,7 @@ struct mtk_flow_entry { + s8 wed_index; + u8 ppe_index; + u16 hash; +- union { +- struct mtk_foe_entry data; +- struct { +- struct mtk_flow_entry *base_flow; +- struct hlist_node list; +- } l2_data; +- }; ++ struct mtk_foe_entry data; + struct rhash_head node; + unsigned long cookie; + }; diff --git a/target/linux/generic/pending-6.12/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch b/target/linux/generic/pending-6.12/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch new file mode 100644 index 00000000000..9141fa69b00 --- /dev/null +++ b/target/linux/generic/pending-6.12/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch @@ -0,0 +1,343 @@ +From: Felix Fietkau +Date: Thu, 23 Mar 2023 11:05:22 +0100 +Subject: [PATCH] net: ethernet: mediatek: fix ppe flow accounting for L2 + flows + +For L2 flows, the packet/byte counters should report the sum of the +counters of their subflows, both current and expired. +In order to make this work, change the way that accounting data is tracked. +Reset counters when a flow enters bind. Once it expires (or enters unbind), +store the last counter value in struct mtk_flow_entry. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -83,9 +83,9 @@ static int mtk_ppe_mib_wait_busy(struct + int ret; + u32 val; + +- ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val, +- !(val & MTK_PPE_MIB_SER_CR_ST), +- 20, MTK_PPE_WAIT_TIMEOUT_US); ++ ret = readl_poll_timeout_atomic(ppe->base + MTK_PPE_MIB_SER_CR, val, ++ !(val & MTK_PPE_MIB_SER_CR_ST), ++ 20, MTK_PPE_WAIT_TIMEOUT_US); + + if (ret) + dev_err(ppe->dev, "MIB table busy"); +@@ -93,17 +93,31 @@ static int mtk_ppe_mib_wait_busy(struct + return ret; + } + +-static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets) ++static inline struct mtk_foe_accounting * ++mtk_ppe_acct_data(struct mtk_ppe *ppe, u16 index) ++{ ++ if (!ppe->acct_table) ++ return NULL; ++ ++ return ppe->acct_table + index * sizeof(struct mtk_foe_accounting); ++} ++ ++struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index) + { + u32 val, cnt_r0, cnt_r1, cnt_r2; ++ struct mtk_foe_accounting *acct; + int ret; + + val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST; + ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val); + ++ acct = mtk_ppe_acct_data(ppe, index); ++ if (!acct) ++ return NULL; ++ + ret = mtk_ppe_mib_wait_busy(ppe); + if (ret) +- return ret; ++ return acct; + + cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0); + cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1); +@@ -112,19 +126,19 @@ static int mtk_mib_entry_read(struct mtk + if (mtk_is_netsys_v3_or_greater(ppe->eth)) { + /* 64 bit for each counter */ + u32 cnt_r3 = readl(ppe->base + MTK_PPE_MIB_SER_R3); +- *bytes = ((u64)cnt_r1 << 32) | cnt_r0; +- *packets = ((u64)cnt_r3 << 32) | cnt_r2; ++ acct->bytes += ((u64)cnt_r1 << 32) | cnt_r0; ++ acct->packets += ((u64)cnt_r3 << 32) | cnt_r2; + } else { + /* 48 bit byte counter, 40 bit packet counter */ + u32 byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0); + u32 byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1); + u32 pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1); + u32 pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2); +- *bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low; +- *packets = ((u64)pkt_cnt_high << 16) | pkt_cnt_low; ++ acct->bytes += ((u64)byte_cnt_high << 32) | byte_cnt_low; ++ acct->packets += ((u64)pkt_cnt_high << 16) | pkt_cnt_low; + } + +- return 0; ++ return acct; + } + + static void mtk_ppe_cache_clear(struct mtk_ppe *ppe) +@@ -522,14 +536,6 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp + hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); + dma_wmb(); + mtk_ppe_cache_clear(ppe); +- +- if (ppe->accounting) { +- struct mtk_foe_accounting *acct; +- +- acct = ppe->acct_table + entry->hash * sizeof(*acct); +- acct->packets = 0; +- acct->bytes = 0; +- } + } + entry->hash = 0xffff; + +@@ -554,11 +560,14 @@ static int __mtk_foe_entry_idle_time(str + } + + static bool +-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ u64 *packets, u64 *bytes) + { ++ struct mtk_foe_accounting *acct; + struct mtk_foe_entry foe = {}; + struct mtk_foe_entry *hwe; + u16 hash = entry->hash; ++ bool ret = false; + int len; + + if (hash == 0xffff) +@@ -569,18 +578,35 @@ mtk_flow_entry_update(struct mtk_ppe *pp + memcpy(&foe, hwe, len); + + if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || +- FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) +- return false; ++ FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) { ++ acct = mtk_ppe_acct_data(ppe, hash); ++ if (acct) { ++ entry->prev_packets += acct->packets; ++ entry->prev_bytes += acct->bytes; ++ } ++ ++ goto out; ++ } + + entry->data.ib1 = foe.ib1; ++ acct = mtk_ppe_mib_entry_read(ppe, hash); ++ ret = true; ++ ++out: ++ if (acct) { ++ *packets += acct->packets; ++ *bytes += acct->bytes; ++ } + +- return true; ++ return ret; + } + + static void + mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); ++ u64 *packets = &entry->packets; ++ u64 *bytes = &entry->bytes; + struct mtk_flow_entry *cur; + struct hlist_node *tmp; + int idle; +@@ -589,7 +615,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe + hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) { + int cur_idle; + +- if (!mtk_flow_entry_update(ppe, cur)) { ++ if (!mtk_flow_entry_update(ppe, cur, packets, bytes)) { ++ entry->prev_packets += cur->prev_packets; ++ entry->prev_bytes += cur->prev_bytes; + __mtk_foe_entry_clear(ppe, entry, false); + continue; + } +@@ -604,10 +632,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe + } + } + ++void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ int *idle) ++{ ++ entry->packets = entry->prev_packets; ++ entry->bytes = entry->prev_bytes; ++ ++ spin_lock_bh(&ppe_lock); ++ ++ if (entry->type == MTK_FLOW_TYPE_L2) ++ mtk_flow_entry_update_l2(ppe, entry); ++ else ++ mtk_flow_entry_update(ppe, entry, &entry->packets, &entry->bytes); ++ ++ *idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); ++ ++ spin_unlock_bh(&ppe_lock); ++} ++ + static void + __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, + u16 hash) + { ++ struct mtk_foe_accounting *acct; + struct mtk_eth *eth = ppe->eth; + u16 timestamp = mtk_eth_timestamp(eth); + struct mtk_foe_entry *hwe; +@@ -638,6 +685,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p + + dma_wmb(); + ++ acct = mtk_ppe_mib_entry_read(ppe, hash); ++ if (acct) { ++ acct->packets = 0; ++ acct->bytes = 0; ++ } ++ + mtk_ppe_cache_clear(ppe); + } + +@@ -802,21 +855,6 @@ out: + spin_unlock_bh(&ppe_lock); + } + +-int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) +-{ +- int idle; +- +- spin_lock_bh(&ppe_lock); +- if (entry->type == MTK_FLOW_TYPE_L2) +- mtk_flow_entry_update_l2(ppe, entry); +- else +- mtk_flow_entry_update(ppe, entry); +- idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); +- spin_unlock_bh(&ppe_lock); +- +- return idle; +-} +- + int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) + { + if (!ppe) +@@ -844,32 +882,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe + return mtk_ppe_wait_busy(ppe); + } + +-struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index, +- struct mtk_foe_accounting *diff) +-{ +- struct mtk_foe_accounting *acct; +- int size = sizeof(struct mtk_foe_accounting); +- u64 bytes, packets; +- +- if (!ppe->accounting) +- return NULL; +- +- if (mtk_mib_entry_read(ppe, index, &bytes, &packets)) +- return NULL; +- +- acct = ppe->acct_table + index * size; +- +- acct->bytes += bytes; +- acct->packets += packets; +- +- if (diff) { +- diff->bytes = bytes; +- diff->packets = packets; +- } +- +- return acct; +-} +- + struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) + { + bool accounting = eth->soc->has_accounting; +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -304,6 +304,8 @@ struct mtk_flow_entry { + struct mtk_foe_entry data; + struct rhash_head node; + unsigned long cookie; ++ u64 prev_packets, prev_bytes; ++ u64 packets, bytes; + }; + + struct mtk_mib_entry { +@@ -348,6 +350,7 @@ void mtk_ppe_deinit(struct mtk_eth *eth) + void mtk_ppe_start(struct mtk_ppe *ppe); + int mtk_ppe_stop(struct mtk_ppe *ppe); + int mtk_ppe_prepare_reset(struct mtk_ppe *ppe); ++struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index); + + void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash); + +@@ -397,9 +400,8 @@ int mtk_foe_entry_set_queue(struct mtk_e + unsigned int queue); + int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); + void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); +-int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); + int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index); +-struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index, +- struct mtk_foe_accounting *diff); ++void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ int *idle); + + #endif +--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c +@@ -97,7 +97,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file + if (bind && state != MTK_FOE_STATE_BIND) + continue; + +- acct = mtk_foe_entry_get_mib(ppe, i, NULL); ++ acct = mtk_ppe_mib_entry_read(ppe, i); + + type = mtk_get_ib1_pkt_type(ppe->eth, entry->ib1); + seq_printf(m, "%05x %s %7s", i, +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -522,24 +522,21 @@ static int + mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) + { + struct mtk_flow_entry *entry; +- struct mtk_foe_accounting diff; +- u32 idle; ++ u64 packets, bytes; ++ int idle; + + entry = rhashtable_lookup(ð->flow_table, &f->cookie, + mtk_flow_ht_params); + if (!entry) + return -ENOENT; + +- idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry); ++ packets = entry->packets; ++ bytes = entry->bytes; ++ mtk_foe_entry_get_stats(eth->ppe[entry->ppe_index], entry, &idle); ++ f->stats.pkts += entry->packets - packets; ++ f->stats.bytes += entry->bytes - bytes; + f->stats.lastused = jiffies - idle * HZ; + +- if (entry->hash != 0xFFFF && +- mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash, +- &diff)) { +- f->stats.pkts += diff.packets; +- f->stats.bytes += diff.bytes; +- } +- + return 0; + } + diff --git a/target/linux/generic/pending-6.12/736-05-net-ethernet-mtk_eth_soc-zero-initialize-PPE-flow-ta.patch b/target/linux/generic/pending-6.12/736-05-net-ethernet-mtk_eth_soc-zero-initialize-PPE-flow-ta.patch new file mode 100644 index 00000000000..2ca5ace3678 --- /dev/null +++ b/target/linux/generic/pending-6.12/736-05-net-ethernet-mtk_eth_soc-zero-initialize-PPE-flow-ta.patch @@ -0,0 +1,27 @@ +From: Felix Fietkau +Date: Fri, 12 Sep 2025 14:18:14 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: zero initialize PPE flow table + +Avoid picking up flows from last boot or other invalid data + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -914,6 +914,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ + if (!foe) + goto err_free_l2_flows; + ++ memset(foe, 0, MTK_PPE_ENTRIES * soc->foe_entry_size); + ppe->foe_table = foe; + + foe_flow_size = (MTK_PPE_ENTRIES / soc->hash_offset) * +@@ -928,6 +929,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ + if (!mib) + return NULL; + ++ memset(mib, 0, MTK_PPE_ENTRIES * sizeof(*mib)); + ppe->mib_table = mib; + + acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct), diff --git a/target/linux/generic/pending-6.12/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch b/target/linux/generic/pending-6.12/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch new file mode 100644 index 00000000000..d76797af92e --- /dev/null +++ b/target/linux/generic/pending-6.12/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch @@ -0,0 +1,893 @@ +From d5e337e7aecc2e1cc9e96768062610adb95f8f72 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:51:14 +0000 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add paths and SerDes modes for + MT7988 + +MT7988 comes with a built-in 2.5G PHY as well as SerDes lanes to +connect external PHYs or transceivers in USXGMII, 10GBase-R, 5GBase-R, +2500Base-X, 1000Base-X and Cisco SGMII interface modes. + +Implement support for configuring for the new paths to SerDes interfaces +and the internal 2.5G PHY. + +Add USXGMII PCS driver for 10GBase-R, 5GBase-R and USXGMII mode, and +setup the new PHYA on MT7988 to access the also still existing old +LynxI PCS for 1000Base-X, 2500Base-X and Cisco SGMII PCS interface +modes. + +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_eth_path.c | 122 +++++++- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 292 +++++++++++++++++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 107 ++++++- + 3 files changed, 470 insertions(+), 51 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_path.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c +@@ -31,10 +31,20 @@ static const char *mtk_eth_path_name(u64 + return "gmac2_rgmii"; + case MTK_ETH_PATH_GMAC2_SGMII: + return "gmac2_sgmii"; ++ case MTK_ETH_PATH_GMAC2_2P5GPHY: ++ return "gmac2_2p5gphy"; + case MTK_ETH_PATH_GMAC2_GEPHY: + return "gmac2_gephy"; ++ case MTK_ETH_PATH_GMAC3_SGMII: ++ return "gmac3_sgmii"; + case MTK_ETH_PATH_GDM1_ESW: + return "gdm1_esw"; ++ case MTK_ETH_PATH_GMAC1_USXGMII: ++ return "gmac1_usxgmii"; ++ case MTK_ETH_PATH_GMAC2_USXGMII: ++ return "gmac2_usxgmii"; ++ case MTK_ETH_PATH_GMAC3_USXGMII: ++ return "gmac3_usxgmii"; + default: + return "unknown path"; + } +@@ -127,6 +137,27 @@ static int set_mux_u3_gmac2_to_qphy(stru + return 0; + } + ++static int set_mux_gmac2_to_2p5gphy(struct mtk_eth *eth, u64 path) ++{ ++ int ret; ++ ++ if (path == MTK_ETH_PATH_GMAC2_2P5GPHY) { ++ ret = regmap_clear_bits(eth->ethsys, ETHSYS_SYSCFG0, SYSCFG0_SGMII_GMAC2_V2); ++ if (ret) ++ return ret; ++ ++ /* Setup mux to 2p5g PHY */ ++ ret = regmap_clear_bits(eth->infra, TOP_MISC_NETSYS_PCS_MUX, MUX_G2_USXGMII_SEL); ++ if (ret) ++ return ret; ++ ++ dev_dbg(eth->dev, "path %s in %s updated\n", ++ mtk_eth_path_name(path), __func__); ++ } ++ ++ return 0; ++} ++ + static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; +@@ -165,7 +196,48 @@ static int set_mux_gmac1_gmac2_to_sgmii_ + return 0; + } + +-static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, u64 path) ++static int set_mux_gmac123_to_usxgmii(struct mtk_eth *eth, u64 path) ++{ ++ unsigned int val = 0; ++ bool updated = true; ++ int mac_id = 0; ++ ++ /* Disable SYSCFG1 SGMII */ ++ regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); ++ ++ switch (path) { ++ case MTK_ETH_PATH_GMAC1_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC1_V2; ++ mac_id = MTK_GMAC1_ID; ++ break; ++ case MTK_ETH_PATH_GMAC2_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2; ++ mac_id = MTK_GMAC2_ID; ++ break; ++ case MTK_ETH_PATH_GMAC3_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC3_V2; ++ mac_id = MTK_GMAC3_ID; ++ break; ++ default: ++ updated = false; ++ }; ++ ++ if (updated) { ++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, ++ SYSCFG0_SGMII_MASK, val); ++ ++ if (mac_id == MTK_GMAC2_ID) ++ regmap_set_bits(eth->infra, TOP_MISC_NETSYS_PCS_MUX, ++ MUX_G2_USXGMII_SEL); ++ } ++ ++ dev_dbg(eth->dev, "path %s in %s updated = %d\n", ++ mtk_eth_path_name(path), __func__, updated); ++ ++ return 0; ++} ++ ++static int set_mux_gmac123_to_gephy_sgmii(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; + bool updated = true; +@@ -182,6 +254,9 @@ static int set_mux_gmac12_to_gephy_sgmii + case MTK_ETH_PATH_GMAC2_SGMII: + val |= SYSCFG0_SGMII_GMAC2_V2; + break; ++ case MTK_ETH_PATH_GMAC3_SGMII: ++ val |= SYSCFG0_SGMII_GMAC3_V2; ++ break; + default: + updated = false; + } +@@ -210,13 +285,25 @@ static const struct mtk_eth_muxc mtk_eth + .cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY, + .set_path = set_mux_u3_gmac2_to_qphy, + }, { ++ .name = "mux_gmac2_to_2p5gphy", ++ .cap_bit = MTK_ETH_MUX_GMAC2_TO_2P5GPHY, ++ .set_path = set_mux_gmac2_to_2p5gphy, ++ }, { + .name = "mux_gmac1_gmac2_to_sgmii_rgmii", + .cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII, + .set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii, + }, { + .name = "mux_gmac12_to_gephy_sgmii", + .cap_bit = MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII, +- .set_path = set_mux_gmac12_to_gephy_sgmii, ++ .set_path = set_mux_gmac123_to_gephy_sgmii, ++ }, { ++ .name = "mux_gmac123_to_gephy_sgmii", ++ .cap_bit = MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII, ++ .set_path = set_mux_gmac123_to_gephy_sgmii, ++ }, { ++ .name = "mux_gmac123_to_usxgmii", ++ .cap_bit = MTK_ETH_MUX_GMAC123_TO_USXGMII, ++ .set_path = set_mux_gmac123_to_usxgmii, + }, + }; + +@@ -249,12 +336,39 @@ out: + return err; + } + ++int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id) ++{ ++ u64 path; ++ ++ path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_USXGMII : ++ (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_USXGMII : ++ MTK_ETH_PATH_GMAC3_USXGMII; ++ ++ /* Setup proper MUXes along the path */ ++ return mtk_eth_mux_setup(eth, path); ++} ++ + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id) + { + u64 path; + +- path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII : +- MTK_ETH_PATH_GMAC2_SGMII; ++ path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_SGMII : ++ (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_SGMII : ++ MTK_ETH_PATH_GMAC3_SGMII; ++ ++ /* Setup proper MUXes along the path */ ++ return mtk_eth_mux_setup(eth, path); ++} ++ ++int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id) ++{ ++ u64 path = 0; ++ ++ if (mac_id == MTK_GMAC2_ID) ++ path = MTK_ETH_PATH_GMAC2_2P5GPHY; ++ ++ if (!path) ++ return -EINVAL; + + /* Setup proper MUXes along the path */ + return mtk_eth_mux_setup(eth, path); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -24,6 +24,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -522,6 +524,30 @@ static void mtk_setup_bridge_switch(stru + MTK_GSW_CFG); + } + ++static bool mtk_check_gmac23_idle(struct mtk_mac *mac) ++{ ++ u32 mac_fsm, gdm_fsm; ++ ++ mac_fsm = mtk_r32(mac->hw, MTK_MAC_FSM(mac->id)); ++ ++ switch (mac->id) { ++ case MTK_GMAC2_ID: ++ gdm_fsm = mtk_r32(mac->hw, MTK_FE_GDM2_FSM); ++ break; ++ case MTK_GMAC3_ID: ++ gdm_fsm = mtk_r32(mac->hw, MTK_FE_GDM3_FSM); ++ break; ++ default: ++ return true; ++ }; ++ ++ if ((mac_fsm & 0xFFFF0000) == 0x01010000 && ++ (gdm_fsm & 0xFFFF0000) == 0x00000000) ++ return true; ++ ++ return false; ++} ++ + static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) + { +@@ -530,6 +556,21 @@ static struct phylink_pcs *mtk_mac_selec + struct mtk_eth *eth = mac->hw; + unsigned int sid; + ++ if (mtk_is_netsys_v3_or_greater(eth)) { ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_SGMII: ++ return mac->sgmii_pcs; ++ case PHY_INTERFACE_MODE_5GBASER: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_USXGMII: ++ return mac->usxgmii_pcs; ++ default: ++ return NULL; ++ } ++ } ++ + if (interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(interface)) { + sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? +@@ -581,7 +622,22 @@ static void mtk_mac_config(struct phylin + goto init_err; + } + break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_5GBASER: ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) { ++ err = mtk_gmac_usxgmii_path_setup(eth, mac->id); ++ if (err) ++ goto init_err; ++ } ++ break; + case PHY_INTERFACE_MODE_INTERNAL: ++ if (mac->id == MTK_GMAC2_ID && ++ MTK_HAS_CAPS(eth->soc->caps, MTK_2P5GPHY)) { ++ err = mtk_gmac_2p5gphy_path_setup(eth, mac->id); ++ if (err) ++ goto init_err; ++ } + break; + default: + goto err_phy; +@@ -628,8 +684,6 @@ static void mtk_mac_config(struct phylin + val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, mac->id); + val |= SYSCFG0_GE_MODE(ge_mode, mac->id); + regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val); +- +- mac->interface = state->interface; + } + + /* SGMII */ +@@ -646,21 +700,40 @@ static void mtk_mac_config(struct phylin + + /* Save the syscfg0 value for mac_finish */ + mac->syscfg0 = val; +- } else if (phylink_autoneg_inband(mode)) { ++ } else if (state->interface != PHY_INTERFACE_MODE_USXGMII && ++ state->interface != PHY_INTERFACE_MODE_10GBASER && ++ state->interface != PHY_INTERFACE_MODE_5GBASER && ++ phylink_autoneg_inband(mode)) { + dev_err(eth->dev, +- "In-band mode not supported in non SGMII mode!\n"); ++ "In-band mode not supported in non-SerDes modes!\n"); + return; + } + + /* Setup gmac */ +- if (mtk_is_netsys_v3_or_greater(eth) && +- mac->interface == PHY_INTERFACE_MODE_INTERNAL) { +- mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id)); +- mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id)); ++ if (mtk_is_netsys_v3_or_greater(eth)) { ++ if (mtk_interface_mode_is_xgmii(state->interface)) { ++ mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id)); ++ mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id)); ++ ++ if (mac->id == MTK_GMAC1_ID) ++ mtk_setup_bridge_switch(eth); ++ } else { ++ mtk_w32(eth, 0, MTK_GDMA_EG_CTRL(mac->id)); + +- mtk_setup_bridge_switch(eth); ++ /* FIXME: In current hardware design, we have to reset FE ++ * when swtiching XGDM to GDM. Therefore, here trigger an SER ++ * to let GDM go back to the initial state. ++ */ ++ if ((mtk_interface_mode_is_xgmii(mac->interface) || ++ mac->interface == PHY_INTERFACE_MODE_NA) && ++ !mtk_check_gmac23_idle(mac) && ++ !test_bit(MTK_RESETTING, ð->state)) ++ schedule_work(ð->pending_work); ++ } + } + ++ mac->interface = state->interface; ++ + return; + + err_phy: +@@ -673,6 +746,18 @@ init_err: + mac->id, phy_modes(state->interface), err); + } + ++static int mtk_mac_prepare(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ ++ if (mac->pextp && mac->interface != interface) ++ phy_reset(mac->pextp); ++ ++ return 0; ++} ++ + static int mtk_mac_finish(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) + { +@@ -681,6 +766,10 @@ static int mtk_mac_finish(struct phylink + struct mtk_eth *eth = mac->hw; + u32 mcr_cur, mcr_new; + ++ /* Setup PMA/PMD */ ++ if (mac->pextp) ++ phy_set_mode_ext(mac->pextp, PHY_MODE_ETHERNET, interface); ++ + /* Enable SGMII */ + if (interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(interface)) +@@ -705,10 +794,14 @@ static void mtk_mac_link_down(struct phy + { + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); +- u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + +- mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK); +- mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); ++ if (!mtk_interface_mode_is_xgmii(interface)) { ++ mtk_m32(mac->hw, MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK, 0, MTK_MAC_MCR(mac->id)); ++ if (mtk_is_netsys_v3_or_greater(mac->hw)) ++ mtk_m32(mac->hw, MTK_XGMAC_FORCE_LINK(mac->id), 0, MTK_XGMAC_STS(mac->id)); ++ } else if (mtk_is_netsys_v3_or_greater(mac->hw) && mac->id != MTK_GMAC1_ID) { ++ mtk_m32(mac->hw, XMAC_MCR_TRX_DISABLE, XMAC_MCR_TRX_DISABLE, MTK_XMAC_MCR(mac->id)); ++ } + } + + static void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx, +@@ -780,13 +873,11 @@ static void mtk_set_queue_speed(struct m + mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs); + } + +-static void mtk_mac_link_up(struct phylink_config *config, +- struct phy_device *phy, +- unsigned int mode, phy_interface_t interface, +- int speed, int duplex, bool tx_pause, bool rx_pause) ++static void mtk_gdm_mac_link_up(struct mtk_mac *mac, ++ struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) + { +- struct mtk_mac *mac = container_of(config, struct mtk_mac, +- phylink_config); + u32 mcr; + + mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); +@@ -830,9 +921,63 @@ static void mtk_mac_link_up(struct phyli + mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); + } + ++static void mtk_xgdm_mac_link_up(struct mtk_mac *mac, ++ struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ u32 mcr, force_link = 0; ++ ++ if (mac->id == MTK_GMAC1_ID) ++ return; ++ ++ /* Eliminate the interference(before link-up) caused by PHY noise */ ++ mtk_m32(mac->hw, XMAC_LOGIC_RST, 0, MTK_XMAC_LOGIC_RST(mac->id)); ++ mdelay(20); ++ mtk_m32(mac->hw, XMAC_GLB_CNTCLR, XMAC_GLB_CNTCLR, MTK_XMAC_CNT_CTRL(mac->id)); ++ ++ if (mac->interface == PHY_INTERFACE_MODE_INTERNAL || mac->id == MTK_GMAC3_ID) ++ force_link = MTK_XGMAC_FORCE_LINK(mac->id); ++ ++ mtk_m32(mac->hw, MTK_XGMAC_FORCE_LINK(mac->id), force_link, MTK_XGMAC_STS(mac->id)); ++ ++ mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id)); ++ mcr &= ~(XMAC_MCR_FORCE_TX_FC | XMAC_MCR_FORCE_RX_FC | XMAC_MCR_TRX_DISABLE); ++ /* Configure pause modes - ++ * phylink will avoid these for half duplex ++ */ ++ if (tx_pause) ++ mcr |= XMAC_MCR_FORCE_TX_FC; ++ if (rx_pause) ++ mcr |= XMAC_MCR_FORCE_RX_FC; ++ ++ mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id)); ++} ++ ++static void mtk_mac_link_up(struct phylink_config *config, ++ struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ ++ if (mtk_is_netsys_v3_or_greater(mac->hw) && mtk_interface_mode_is_xgmii(interface)) ++ mtk_xgdm_mac_link_up(mac, phy, mode, interface, speed, duplex, ++ tx_pause, rx_pause); ++ else ++ mtk_gdm_mac_link_up(mac, phy, mode, interface, speed, duplex, ++ tx_pause, rx_pause); ++ ++ /* Repeat pextp setup to tune link */ ++ if (mac->pextp) ++ phy_set_mode_ext(mac->pextp, PHY_MODE_ETHERNET, interface); ++} ++ + static const struct phylink_mac_ops mtk_phylink_ops = { + .mac_select_pcs = mtk_mac_select_pcs, + .mac_config = mtk_mac_config, ++ .mac_prepare = mtk_mac_prepare, + .mac_finish = mtk_mac_finish, + .mac_link_down = mtk_mac_link_down, + .mac_link_up = mtk_mac_link_up, +@@ -3591,6 +3736,9 @@ static int mtk_open(struct net_device *d + + ppe_num = eth->soc->ppe_num; + ++ if (mac->pextp) ++ phy_power_on(mac->pextp); ++ + err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0); + if (err) { + netdev_err(dev, "%s: could not attach PHY: %d\n", __func__, +@@ -3738,6 +3886,9 @@ static int mtk_stop(struct net_device *d + for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) + mtk_ppe_stop(eth->ppe[i]); + ++ if (mac->pextp) ++ phy_power_off(mac->pextp); ++ + return 0; + } + +@@ -4837,6 +4988,7 @@ static const struct net_device_ops mtk_n + static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) + { + const __be32 *_id = of_get_property(np, "reg", NULL); ++ struct device_node *pcs_np; + phy_interface_t phy_mode; + struct phylink *phylink; + struct mtk_mac *mac; +@@ -4875,16 +5027,44 @@ static int mtk_add_mac(struct mtk_eth *e + mac->id = id; + mac->hw = eth; + mac->of_node = np; ++ pcs_np = of_parse_phandle(mac->of_node, "pcs-handle", 0); ++ if (pcs_np) { ++ mac->sgmii_pcs = mtk_pcs_lynxi_get(eth->dev, pcs_np); ++ if (IS_ERR(mac->sgmii_pcs)) { ++ if (PTR_ERR(mac->sgmii_pcs) != -EPROBE_DEFER) ++ dev_err(eth->dev, ++ "cannot select SGMII PCS, error %ld\n", ++ PTR_ERR(mac->sgmii_pcs)); ++ ++ err = PTR_ERR(mac->sgmii_pcs); ++ goto free_netdev; ++ } ++ } + +- err = of_get_ethdev_address(mac->of_node, eth->netdev[id]); +- if (err == -EPROBE_DEFER) +- return err; ++ pcs_np = of_parse_phandle(mac->of_node, "pcs-handle", 1); ++ if (pcs_np) { ++ mac->usxgmii_pcs = mtk_usxgmii_pcs_get(eth->dev, pcs_np); ++ if (IS_ERR(mac->usxgmii_pcs)) { ++ if (PTR_ERR(mac->usxgmii_pcs) != -EPROBE_DEFER) ++ dev_err(eth->dev, ++ "cannot select USXGMII PCS, error %ld\n", ++ PTR_ERR(mac->usxgmii_pcs)); ++ ++ err = PTR_ERR(mac->usxgmii_pcs); ++ goto free_netdev; ++ } ++ } + +- if (err) { +- /* If the mac address is invalid, use random mac address */ +- eth_hw_addr_random(eth->netdev[id]); +- dev_err(eth->dev, "generated random MAC address %pM\n", +- eth->netdev[id]->dev_addr); ++ if (mtk_is_netsys_v3_or_greater(eth) && (mac->sgmii_pcs || mac->usxgmii_pcs)) { ++ mac->pextp = devm_of_phy_optional_get(eth->dev, mac->of_node, NULL); ++ if (IS_ERR(mac->pextp)) { ++ if (PTR_ERR(mac->pextp) != -EPROBE_DEFER) ++ dev_err(eth->dev, "cannot get PHY, error %ld\n", ++ PTR_ERR(mac->pextp)); ++ ++ err = PTR_ERR(mac->pextp); ++ goto free_netdev; ++ } + } + + memset(mac->hwlro_ip, 0, sizeof(mac->hwlro_ip)); +@@ -4967,8 +5147,21 @@ static int mtk_add_mac(struct mtk_eth *e + phy_interface_zero(mac->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + mac->phylink_config.supported_interfaces); ++ } else if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_USXGMII)) { ++ mac->phylink_config.mac_capabilities |= MAC_5000FD | MAC_10000FD; ++ __set_bit(PHY_INTERFACE_MODE_5GBASER, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_USXGMII, ++ mac->phylink_config.supported_interfaces); + } + ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) && ++ id == MTK_GMAC2_ID) ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ mac->phylink_config.supported_interfaces); ++ + phylink = phylink_create(&mac->phylink_config, + of_fwnode_handle(mac->of_node), + phy_mode, &mtk_phylink_ops); +@@ -5019,6 +5212,26 @@ free_netdev: + return err; + } + ++static int mtk_mac_assign_address(struct mtk_eth *eth, int i, bool test_defer_only) ++{ ++ int err = of_get_ethdev_address(eth->mac[i]->of_node, eth->netdev[i]); ++ ++ if (err == -EPROBE_DEFER) ++ return err; ++ ++ if (test_defer_only) ++ return 0; ++ ++ if (err) { ++ /* If the mac address is invalid, use random mac address */ ++ eth_hw_addr_random(eth->netdev[i]); ++ dev_err(eth->dev, "generated random MAC address %pM\n", ++ eth->netdev[i]); ++ } ++ ++ return 0; ++} ++ + void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev) + { + struct net_device *dev, *tmp; +@@ -5165,7 +5378,8 @@ static int mtk_probe(struct platform_dev + regmap_write(cci, 0, 3); + } + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII) && ++ !mtk_is_netsys_v3_or_greater(eth)) { + err = mtk_sgmii_init(eth); + + if (err) +@@ -5276,6 +5490,24 @@ static int mtk_probe(struct platform_dev + } + } + ++ for (i = 0; i < MTK_MAX_DEVS; i++) { ++ if (!eth->netdev[i]) ++ continue; ++ ++ err = mtk_mac_assign_address(eth, i, true); ++ if (err) ++ goto err_deinit_hw; ++ } ++ ++ for (i = 0; i < MTK_MAX_DEVS; i++) { ++ if (!eth->netdev[i]) ++ continue; ++ ++ err = mtk_mac_assign_address(eth, i, false); ++ if (err) ++ goto err_deinit_hw; ++ } ++ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) { + err = devm_request_irq(eth->dev, eth->irq[0], + mtk_handle_irq, 0, +@@ -5386,6 +5618,11 @@ static void mtk_remove(struct platform_d + mtk_stop(eth->netdev[i]); + mac = netdev_priv(eth->netdev[i]); + phylink_disconnect_phy(mac->phylink); ++ if (mac->sgmii_pcs) ++ mtk_pcs_lynxi_put(mac->sgmii_pcs); ++ ++ if (mac->usxgmii_pcs) ++ mtk_usxgmii_pcs_put(mac->usxgmii_pcs); + } + + mtk_wed_exit(); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -527,6 +528,21 @@ + #define INTF_MODE_RGMII_1000 (TRGMII_MODE | TRGMII_CENTRAL_ALIGNED) + #define INTF_MODE_RGMII_10_100 0 + ++/* XFI Mac control registers */ ++#define MTK_XMAC_BASE(x) (0x12000 + (((x) - 1) * 0x1000)) ++#define MTK_XMAC_MCR(x) (MTK_XMAC_BASE(x)) ++#define XMAC_MCR_TRX_DISABLE 0xf ++#define XMAC_MCR_FORCE_TX_FC BIT(5) ++#define XMAC_MCR_FORCE_RX_FC BIT(4) ++ ++/* XFI Mac logic reset registers */ ++#define MTK_XMAC_LOGIC_RST(x) (MTK_XMAC_BASE(x) + 0x10) ++#define XMAC_LOGIC_RST BIT(0) ++ ++/* XFI Mac count global control */ ++#define MTK_XMAC_CNT_CTRL(x) (MTK_XMAC_BASE(x) + 0x100) ++#define XMAC_GLB_CNTCLR BIT(0) ++ + /* GPIO port control registers for GMAC 2*/ + #define GPIO_OD33_CTRL8 0x4c0 + #define GPIO_BIAS_CTRL 0xed0 +@@ -552,6 +568,7 @@ + #define SYSCFG0_SGMII_GMAC2 ((3 << 8) & SYSCFG0_SGMII_MASK) + #define SYSCFG0_SGMII_GMAC1_V2 BIT(9) + #define SYSCFG0_SGMII_GMAC2_V2 BIT(8) ++#define SYSCFG0_SGMII_GMAC3_V2 BIT(7) + + + /* ethernet subsystem clock register */ +@@ -590,6 +607,11 @@ + #define GEPHY_MAC_SEL BIT(1) + + /* Top misc registers */ ++#define TOP_MISC_NETSYS_PCS_MUX 0x0 ++#define NETSYS_PCS_MUX_MASK GENMASK(1, 0) ++#define MUX_G2_USXGMII_SEL BIT(1) ++#define MUX_HSGMII1_G1_SEL BIT(0) ++ + #define USB_PHY_SWITCH_REG 0x218 + #define QPHY_SEL_MASK GENMASK(1, 0) + #define SGMII_QPHY_SEL 0x2 +@@ -614,6 +636,8 @@ + #define MT7628_SDM_RBCNT (MT7628_SDM_OFFSET + 0x10c) + #define MT7628_SDM_CS_ERR (MT7628_SDM_OFFSET + 0x110) + ++/* Debug Purpose Register */ ++#define MTK_PSE_FQFC_CFG 0x100 + #define MTK_FE_CDM1_FSM 0x220 + #define MTK_FE_CDM2_FSM 0x224 + #define MTK_FE_CDM3_FSM 0x238 +@@ -622,6 +646,11 @@ + #define MTK_FE_CDM6_FSM 0x328 + #define MTK_FE_GDM1_FSM 0x228 + #define MTK_FE_GDM2_FSM 0x22C ++#define MTK_FE_GDM3_FSM 0x23C ++#define MTK_FE_PSE_FREE 0x240 ++#define MTK_FE_DROP_FQ 0x244 ++#define MTK_FE_DROP_FC 0x248 ++#define MTK_FE_DROP_PPE 0x24C + + #define MTK_MAC_FSM(x) (0x1010C + ((x) * 0x100)) + +@@ -945,6 +974,8 @@ enum mkt_eth_capabilities { + MTK_RGMII_BIT = 0, + MTK_TRGMII_BIT, + MTK_SGMII_BIT, ++ MTK_USXGMII_BIT, ++ MTK_2P5GPHY_BIT, + MTK_ESW_BIT, + MTK_GEPHY_BIT, + MTK_MUX_BIT, +@@ -965,8 +996,11 @@ enum mkt_eth_capabilities { + MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, + MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT, + MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT, ++ MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT, + MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, + MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT, ++ MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT, ++ MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT, + + /* PATH BITS */ + MTK_ETH_PATH_GMAC1_RGMII_BIT, +@@ -974,14 +1008,21 @@ enum mkt_eth_capabilities { + MTK_ETH_PATH_GMAC1_SGMII_BIT, + MTK_ETH_PATH_GMAC2_RGMII_BIT, + MTK_ETH_PATH_GMAC2_SGMII_BIT, ++ MTK_ETH_PATH_GMAC2_2P5GPHY_BIT, + MTK_ETH_PATH_GMAC2_GEPHY_BIT, ++ MTK_ETH_PATH_GMAC3_SGMII_BIT, + MTK_ETH_PATH_GDM1_ESW_BIT, ++ MTK_ETH_PATH_GMAC1_USXGMII_BIT, ++ MTK_ETH_PATH_GMAC2_USXGMII_BIT, ++ MTK_ETH_PATH_GMAC3_USXGMII_BIT, + }; + + /* Supported hardware group on SoCs */ + #define MTK_RGMII BIT_ULL(MTK_RGMII_BIT) + #define MTK_TRGMII BIT_ULL(MTK_TRGMII_BIT) + #define MTK_SGMII BIT_ULL(MTK_SGMII_BIT) ++#define MTK_USXGMII BIT_ULL(MTK_USXGMII_BIT) ++#define MTK_2P5GPHY BIT_ULL(MTK_2P5GPHY_BIT) + #define MTK_ESW BIT_ULL(MTK_ESW_BIT) + #define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT) + #define MTK_MUX BIT_ULL(MTK_MUX_BIT) +@@ -1004,10 +1045,16 @@ enum mkt_eth_capabilities { + BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) + #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY \ + BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) ++#define MTK_ETH_MUX_GMAC2_TO_2P5GPHY \ ++ BIT_ULL(MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT) + #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) + #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT) ++#define MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII \ ++ BIT_ULL(MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT) ++#define MTK_ETH_MUX_GMAC123_TO_USXGMII \ ++ BIT_ULL(MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT) + + /* Supported path present on SoCs */ + #define MTK_ETH_PATH_GMAC1_RGMII BIT_ULL(MTK_ETH_PATH_GMAC1_RGMII_BIT) +@@ -1015,8 +1062,13 @@ enum mkt_eth_capabilities { + #define MTK_ETH_PATH_GMAC1_SGMII BIT_ULL(MTK_ETH_PATH_GMAC1_SGMII_BIT) + #define MTK_ETH_PATH_GMAC2_RGMII BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT) + #define MTK_ETH_PATH_GMAC2_SGMII BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_2P5GPHY BIT_ULL(MTK_ETH_PATH_GMAC2_2P5GPHY_BIT) + #define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT) ++#define MTK_ETH_PATH_GMAC3_SGMII BIT_ULL(MTK_ETH_PATH_GMAC3_SGMII_BIT) + #define MTK_ETH_PATH_GDM1_ESW BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT) ++#define MTK_ETH_PATH_GMAC1_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC1_USXGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC2_USXGMII_BIT) ++#define MTK_ETH_PATH_GMAC3_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC3_USXGMII_BIT) + + #define MTK_GMAC1_RGMII (MTK_ETH_PATH_GMAC1_RGMII | MTK_RGMII) + #define MTK_GMAC1_TRGMII (MTK_ETH_PATH_GMAC1_TRGMII | MTK_TRGMII) +@@ -1024,7 +1076,12 @@ enum mkt_eth_capabilities { + #define MTK_GMAC2_RGMII (MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII) + #define MTK_GMAC2_SGMII (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII) + #define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) ++#define MTK_GMAC2_2P5GPHY (MTK_ETH_PATH_GMAC2_2P5GPHY | MTK_2P5GPHY) ++#define MTK_GMAC3_SGMII (MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII) + #define MTK_GDM1_ESW (MTK_ETH_PATH_GDM1_ESW | MTK_ESW) ++#define MTK_GMAC1_USXGMII (MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII) ++#define MTK_GMAC2_USXGMII (MTK_ETH_PATH_GMAC2_USXGMII | MTK_USXGMII) ++#define MTK_GMAC3_USXGMII (MTK_ETH_PATH_GMAC3_USXGMII | MTK_USXGMII) + + /* MUXes present on SoCs */ + /* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */ +@@ -1043,10 +1100,20 @@ enum mkt_eth_capabilities { + (MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_MUX | \ + MTK_SHARED_SGMII) + ++/* 2: GMAC2 -> XGMII */ ++#define MTK_MUX_GMAC2_TO_2P5GPHY \ ++ (MTK_ETH_MUX_GMAC2_TO_2P5GPHY | MTK_MUX | MTK_INFRA) ++ + /* 0: GMACx -> GEPHY, 1: GMACx -> SGMII where x is 1 or 2 */ + #define MTK_MUX_GMAC12_TO_GEPHY_SGMII \ + (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX) + ++#define MTK_MUX_GMAC123_TO_GEPHY_SGMII \ ++ (MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII | MTK_MUX) ++ ++#define MTK_MUX_GMAC123_TO_USXGMII \ ++ (MTK_ETH_MUX_GMAC123_TO_USXGMII | MTK_MUX | MTK_INFRA) ++ + #define MTK_HAS_CAPS(caps, _x) (((caps) & (_x)) == (_x)) + + #define MT7621_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \ +@@ -1078,8 +1145,12 @@ enum mkt_eth_capabilities { + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ + MTK_RSTCTRL_PPE1 | MTK_SRAM) + +-#define MT7988_CAPS (MTK_36BIT_DMA | MTK_GDM1_ESW | MTK_QDMA | \ +- MTK_RSTCTRL_PPE1 | MTK_RSTCTRL_PPE2 | MTK_SRAM) ++#define MT7988_CAPS (MTK_36BIT_DMA | MTK_GDM1_ESW | MTK_GMAC1_SGMII | \ ++ MTK_GMAC2_2P5GPHY | MTK_GMAC2_SGMII | MTK_GMAC2_USXGMII | \ ++ MTK_GMAC3_SGMII | MTK_GMAC3_USXGMII | \ ++ MTK_MUX_GMAC123_TO_GEPHY_SGMII | \ ++ MTK_MUX_GMAC123_TO_USXGMII | MTK_MUX_GMAC2_TO_2P5GPHY | \ ++ MTK_QDMA | MTK_RSTCTRL_PPE1 | MTK_RSTCTRL_PPE2 | MTK_SRAM) + + struct mtk_tx_dma_desc_info { + dma_addr_t addr; +@@ -1327,6 +1398,9 @@ struct mtk_mac { + struct device_node *of_node; + struct phylink *phylink; + struct phylink_config phylink_config; ++ struct phylink_pcs *sgmii_pcs; ++ struct phylink_pcs *usxgmii_pcs; ++ struct phy *pextp; + struct mtk_eth *hw; + struct mtk_hw_stats *hw_stats; + __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; +@@ -1450,6 +1524,19 @@ static inline u32 mtk_get_ib2_multicast_ + return MTK_FOE_IB2_MULTICAST; + } + ++static inline bool mtk_interface_mode_is_xgmii(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_INTERNAL: ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_5GBASER: ++ return true; ++ default: ++ return false; ++ } ++} ++ + /* read the hardware status register */ + void mtk_stats_update_mac(struct mtk_mac *mac); + +@@ -1458,8 +1545,10 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne + u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned int reg); + + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); ++int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id); + int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id); + int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id); ++int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id); + + int mtk_eth_offload_init(struct mtk_eth *eth, u8 id); + int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, diff --git a/target/linux/generic/pending-6.12/738-01-net-ethernet-mtk_eth_soc-reduce-rx-ring-size-for-older.patch b/target/linux/generic/pending-6.12/738-01-net-ethernet-mtk_eth_soc-reduce-rx-ring-size-for-older.patch new file mode 100644 index 00000000000..95abd9459bc --- /dev/null +++ b/target/linux/generic/pending-6.12/738-01-net-ethernet-mtk_eth_soc-reduce-rx-ring-size-for-older.patch @@ -0,0 +1,104 @@ +From: Felix Fietkau +Subject: [PATCH net-next 3/4] net: ethernet: mtk_eth_soc: reduce rx ring size for older chipsets +Date: Tue, 15 Oct 2024 13:09:37 +0200 + +Commit c57e55819443 ("net: ethernet: mtk_eth_soc: handle dma buffer +size soc specific") resolved some tx timeout issues by bumping FQ and +tx ring sizes from 512 to 2048 entries (the value used in the MediaTek +SDK), however it also changed the rx ring size for all chipsets in the +same way. + +Based on a few tests, it seems that a symmetric rx/tx ring size of 2048 +really only makes sense on MT7988, which is capable of 10G ethernet links. + +Older chipsets are typically deployed in systems that are more memory +constrained and don't actually need the larger rings to handle received +packets. + +In order to reduce wasted memory set the ring size based on the SoC to +the following values: +- 2048 on MT7988 +- 1024 on MT7986 +- 512 (previous value) on everything else, except: +- 256 on RT5350 (the oldest supported chipset) + +Fixes: c57e55819443 ("net: ethernet: mtk_eth_soc: handle dma buffer size soc specific") +Signed-off-by: Felix Fietkau +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -5656,7 +5656,7 @@ static const struct mtk_soc_data mt2701_ + DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID, +- .dma_size = MTK_DMA_SIZE(2K), ++ .dma_size = MTK_DMA_SIZE(512), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + }, +@@ -5684,7 +5684,7 @@ static const struct mtk_soc_data mt7621_ + DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID, +- .dma_size = MTK_DMA_SIZE(2K), ++ .dma_size = MTK_DMA_SIZE(512), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + }, +@@ -5714,7 +5714,7 @@ static const struct mtk_soc_data mt7622_ + DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID, +- .dma_size = MTK_DMA_SIZE(2K), ++ .dma_size = MTK_DMA_SIZE(512), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + }, +@@ -5743,7 +5743,7 @@ static const struct mtk_soc_data mt7623_ + DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID, +- .dma_size = MTK_DMA_SIZE(2K), ++ .dma_size = MTK_DMA_SIZE(512), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + }, +@@ -5769,7 +5769,7 @@ static const struct mtk_soc_data mt7629_ + DESC_SIZE(struct mtk_rx_dma), + .irq_done_mask = MTK_RX_DONE_INT, + .dma_l4_valid = RX_DMA_L4_VALID, +- .dma_size = MTK_DMA_SIZE(2K), ++ .dma_size = MTK_DMA_SIZE(512), + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, + }, +@@ -5801,7 +5801,7 @@ static const struct mtk_soc_data mt7981_ + .dma_l4_valid = RX_DMA_L4_VALID_V2, + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, +- .dma_size = MTK_DMA_SIZE(2K), ++ .dma_size = MTK_DMA_SIZE(512), + }, + }; + +@@ -5831,7 +5831,7 @@ static const struct mtk_soc_data mt7986_ + .dma_l4_valid = RX_DMA_L4_VALID_V2, + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, +- .dma_size = MTK_DMA_SIZE(2K), ++ .dma_size = MTK_DMA_SIZE(1K), + }, + }; + +@@ -5884,7 +5884,7 @@ static const struct mtk_soc_data rt5350_ + .dma_l4_valid = RX_DMA_L4_VALID_PDMA, + .dma_max_len = MTK_TX_DMA_BUF_LEN, + .dma_len_offset = 16, +- .dma_size = MTK_DMA_SIZE(2K), ++ .dma_size = MTK_DMA_SIZE(256), + }, + }; + diff --git a/target/linux/generic/pending-6.12/738-02-net-ethernet-mtk_eth_soc-do-not-enable-page-pool-sta.patch b/target/linux/generic/pending-6.12/738-02-net-ethernet-mtk_eth_soc-do-not-enable-page-pool-sta.patch new file mode 100644 index 00000000000..53216caeaa0 --- /dev/null +++ b/target/linux/generic/pending-6.12/738-02-net-ethernet-mtk_eth_soc-do-not-enable-page-pool-sta.patch @@ -0,0 +1,43 @@ +From: Danila Romanov +Date: Wed, 22 Jan 2025 06:48:45 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: do not enable page pool stats by + default + +There is no reason for it to be enabled by default. +Align mtk_eth_soc driver to mt76 driver. + +This option incurs additional CPU cost in allocation and recycle paths +and additional memory cost to store the statistics. + +Signed-off-by: Danila Romanov +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -26,7 +26,6 @@ config NET_MEDIATEK_SOC + select PHYLINK + select DIMLIB + select PAGE_POOL +- select PAGE_POOL_STATS + select PCS_MTK_LYNXI + select REGMAP_MMIO + help +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -4756,6 +4756,7 @@ static int mtk_get_sset_count(struct net + + static void mtk_ethtool_pp_stats(struct mtk_eth *eth, u64 *data) + { ++#ifdef CONFIG_PAGE_POOL_STATS + struct page_pool_stats stats = {}; + int i; + +@@ -4768,6 +4769,7 @@ static void mtk_ethtool_pp_stats(struct + page_pool_get_stats(ring->page_pool, &stats); + } + page_pool_ethtool_stats_get(data, &stats); ++#endif + } + + static void mtk_get_ethtool_stats(struct net_device *dev, diff --git a/target/linux/generic/pending-6.12/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch b/target/linux/generic/pending-6.12/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch new file mode 100644 index 00000000000..1f1c40b1d91 --- /dev/null +++ b/target/linux/generic/pending-6.12/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch @@ -0,0 +1,136 @@ +From patchwork Thu Feb 1 21:52:20 2024 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 13541842 +Date: Thu, 1 Feb 2024 21:52:20 +0000 +From: Daniel Golle +To: Bc-bocun Chen , + Steven Liu , + John Crispin , + Chunfeng Yun , + Vinod Koul , + Kishon Vijay Abraham I , + Rob Herring , + Krzysztof Kozlowski , + Conor Dooley , + Daniel Golle , + Qingfang Deng , + SkyLake Huang , + Matthias Brugger , + AngeloGioacchino Del Regno , + Philipp Zabel , + linux-arm-kernel@lists.infradead.org, + linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, + netdev@vger.kernel.org +Subject: [PATCH 1/2] dt-bindings: phy: mediatek,xfi-tphy: add new bindings +Message-ID: + <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org> +MIME-Version: 1.0 +Content-Disposition: inline +List-Id: Linux Phy Mailing list + +Add bindings for the MediaTek XFI T-PHY Ethernet SerDes PHY found in the +MediaTek MT7988 SoC which can operate at various interfaces modes: + +via USXGMII PCS: + * USXGMII + * 10GBase-R + * 5GBase-R + +via LynxI SGMII PCS: + * 2500Base-X + * 1000Base-X + * Cisco SGMII (MAC side) + +Signed-off-by: Daniel Golle +--- + .../bindings/phy/mediatek,xfi-tphy.yaml | 80 +++++++++++++++++++ + 1 file changed, 80 insertions(+) + create mode 100644 Documentation/devicetree/bindings/phy/mediatek,xfi-tphy.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/mediatek,xfi-tphy.yaml +@@ -0,0 +1,80 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/phy/mediatek,xfi-tphy.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: MediaTek XFI T-PHY ++ ++maintainers: ++ - Daniel Golle ++ ++description: ++ The MediaTek XFI SerDes T-PHY provides the physical SerDes lanes ++ used by the (10G/5G) USXGMII PCS and (1G/2.5G) LynxI PCS found in ++ MediaTek's 10G-capabale SoCs. ++ ++properties: ++ $nodename: ++ pattern: "^phy@[0-9a-f]+$" ++ ++ compatible: ++ const: mediatek,mt7988-xfi-tphy ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: XFI PHY clock ++ - description: XFI register clock ++ ++ clock-names: ++ items: ++ - const: xfipll ++ - const: topxtal ++ ++ resets: ++ items: ++ - description: PEXTP reset ++ ++ mediatek,usxgmii-performance-errata: ++ $ref: /schemas/types.yaml#/definitions/flag ++ description: ++ One instance of the T-PHY on MT7988 suffers from a performance ++ problem in 10GBase-R mode which needs a work-around in the driver. ++ The work-around is enabled using this flag. ++ ++ "#phy-cells": ++ const: 0 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-names ++ - resets ++ - "#phy-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ soc { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ phy@11f20000 { ++ compatible = "mediatek,mt7988-xfi-tphy"; ++ reg = <0 0x11f20000 0 0x10000>; ++ clocks = <&xfi_pll CLK_XFIPLL_PLL_EN>, ++ <&topckgen CLK_TOP_XFI_PHY_0_XTAL_SEL>; ++ clock-names = "xfipll", "topxtal"; ++ resets = <&watchdog 14>; ++ mediatek,usxgmii-performance-errata; ++ #phy-cells = <0>; ++ }; ++ }; ++ ++... diff --git a/target/linux/generic/pending-6.12/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch b/target/linux/generic/pending-6.12/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch new file mode 100644 index 00000000000..0e4a63ec7fb --- /dev/null +++ b/target/linux/generic/pending-6.12/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch @@ -0,0 +1,369 @@ +From 4b1a2716299c0e96a698044aebf3f80513509ae7 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:47:18 +0000 +Subject: [PATCH 3/5] net: pcs: pcs-mtk-lynxi: add platform driver for MT7988 + +Introduce a proper platform MFD driver for the LynxI (H)SGMII PCS which +is going to initially be used for the MT7988 SoC. + +Signed-off-by: Daniel Golle +--- + drivers/net/pcs/pcs-mtk-lynxi.c | 227 ++++++++++++++++++++++++++++-- + include/linux/pcs/pcs-mtk-lynxi.h | 11 ++ + 2 files changed, 227 insertions(+), 11 deletions(-) + +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + // Copyright (c) 2018-2019 MediaTek Inc. +-/* A library for MediaTek SGMII circuit ++/* A library and platform driver for the MediaTek LynxI SGMII circuit + * + * Author: Sean Wang + * Author: Alexander Couzens +@@ -8,11 +8,17 @@ + * + */ + ++#include + #include ++#include ++#include + #include ++#include + #include + #include ++#include + #include ++#include + + /* SGMII subsystem config registers */ + /* BMCR (low 16) BMSR (high 16) */ +@@ -65,6 +71,8 @@ + #define SGMII_PN_SWAP_MASK GENMASK(1, 0) + #define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) + ++#define MTK_NETSYS_V3_AMA_RGC3 0x128 ++ + /* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated + * data + * @regmap: The register map pointing at the range used to setup +@@ -74,15 +82,29 @@ + * @interface: Currently configured interface mode + * @pcs: Phylink PCS structure + * @flags: Flags indicating hardware properties ++ * @rstc: Reset controller ++ * @sgmii_sel: SGMII Register Clock ++ * @sgmii_rx: SGMII RX Clock ++ * @sgmii_tx: SGMII TX Clock ++ * @node: List node + */ + struct mtk_pcs_lynxi { + struct regmap *regmap; ++ struct device *dev; + u32 ana_rgc3; + phy_interface_t interface; + struct phylink_pcs pcs; + u32 flags; ++ struct reset_control *rstc; ++ struct clk *sgmii_sel; ++ struct clk *sgmii_rx; ++ struct clk *sgmii_tx; ++ struct list_head node; + }; + ++static LIST_HEAD(mtk_pcs_lynxi_instances); ++static DEFINE_MUTEX(instance_mutex); ++ + static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs) + { + return container_of(pcs, struct mtk_pcs_lynxi, pcs); +@@ -117,6 +139,17 @@ static void mtk_pcs_lynxi_get_state(stru + FIELD_GET(SGMII_LPA, adv)); + } + ++static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs) ++{ ++ if (!mpcs->rstc) ++ return; ++ ++ reset_control_assert(mpcs->rstc); ++ udelay(100); ++ reset_control_deassert(mpcs->rstc); ++ mdelay(1); ++} ++ + static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, +@@ -162,6 +195,7 @@ static int mtk_pcs_lynxi_config(struct p + SGMII_PHYA_PWD); + + /* Reset SGMII PCS state */ ++ mtk_sgmii_reset(mpcs); + regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0, + SGMII_SW_RESET); + +@@ -248,10 +282,29 @@ static void mtk_pcs_lynxi_link_up(struct + } + } + ++static int mtk_pcs_lynxi_enable(struct phylink_pcs *pcs) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ ++ if (mpcs->sgmii_tx && mpcs->sgmii_rx) { ++ clk_prepare_enable(mpcs->sgmii_rx); ++ clk_prepare_enable(mpcs->sgmii_tx); ++ } ++ ++ return 0; ++} ++ + static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs) + { + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + ++ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD); ++ ++ if (mpcs->sgmii_tx && mpcs->sgmii_rx) { ++ clk_disable_unprepare(mpcs->sgmii_tx); ++ clk_disable_unprepare(mpcs->sgmii_rx); ++ } ++ + mpcs->interface = PHY_INTERFACE_MODE_NA; + } + +@@ -262,11 +315,12 @@ static const struct phylink_pcs_ops mtk_ + .pcs_an_restart = mtk_pcs_lynxi_restart_an, + .pcs_link_up = mtk_pcs_lynxi_link_up, + .pcs_disable = mtk_pcs_lynxi_disable, ++ .pcs_enable = mtk_pcs_lynxi_enable, + }; + +-struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, +- struct regmap *regmap, u32 ana_rgc3, +- u32 flags) ++static struct phylink_pcs *mtk_pcs_lynxi_init(struct device *dev, struct regmap *regmap, ++ u32 ana_rgc3, u32 flags, ++ struct mtk_pcs_lynxi *prealloc) + { + struct mtk_pcs_lynxi *mpcs; + u32 id, ver; +@@ -274,29 +328,33 @@ struct phylink_pcs *mtk_pcs_lynxi_create + + ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id); + if (ret < 0) +- return NULL; ++ return ERR_PTR(ret); + + if (id != SGMII_LYNXI_DEV_ID) { + dev_err(dev, "unknown PCS device id %08x\n", id); +- return NULL; ++ return ERR_PTR(-ENODEV); + } + + ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver); + if (ret < 0) +- return NULL; ++ return ERR_PTR(ret); + + ver = FIELD_GET(SGMII_DEV_VERSION, ver); + if (ver != 0x1) { + dev_err(dev, "unknown PCS device version %04x\n", ver); +- return NULL; ++ return ERR_PTR(-ENODEV); + } + + dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id, + ver); + +- mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); +- if (!mpcs) +- return NULL; ++ if (prealloc) { ++ mpcs = prealloc; ++ } else { ++ mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return ERR_PTR(-ENOMEM); ++ }; + + mpcs->ana_rgc3 = ana_rgc3; + mpcs->regmap = regmap; +@@ -307,6 +365,13 @@ struct phylink_pcs *mtk_pcs_lynxi_create + mpcs->interface = PHY_INTERFACE_MODE_NA; + + return &mpcs->pcs; ++}; ++ ++struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, ++ struct regmap *regmap, u32 ana_rgc3, ++ u32 flags) ++{ ++ return mtk_pcs_lynxi_init(dev, regmap, ana_rgc3, flags, NULL); + } + EXPORT_SYMBOL(mtk_pcs_lynxi_create); + +@@ -319,5 +384,142 @@ void mtk_pcs_lynxi_destroy(struct phylin + } + EXPORT_SYMBOL(mtk_pcs_lynxi_destroy); + ++static int mtk_pcs_lynxi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct mtk_pcs_lynxi *mpcs; ++ struct phylink_pcs *pcs; ++ struct regmap *regmap; ++ u32 flags = 0; ++ ++ mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return -ENOMEM; ++ ++ mpcs->dev = dev; ++ regmap = syscon_node_to_regmap(np->parent); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ if (of_property_read_bool(np->parent, "mediatek,pnswap")) ++ flags |= MTK_SGMII_FLAG_PN_SWAP; ++ ++ mpcs->rstc = of_reset_control_get_shared(np->parent, NULL); ++ if (IS_ERR(mpcs->rstc)) ++ return PTR_ERR(mpcs->rstc); ++ ++ reset_control_deassert(mpcs->rstc); ++ mpcs->sgmii_sel = devm_clk_get_enabled(dev, "sgmii_sel"); ++ if (IS_ERR(mpcs->sgmii_sel)) ++ return PTR_ERR(mpcs->sgmii_sel); ++ ++ mpcs->sgmii_rx = devm_clk_get(dev, "sgmii_rx"); ++ if (IS_ERR(mpcs->sgmii_rx)) ++ return PTR_ERR(mpcs->sgmii_rx); ++ ++ mpcs->sgmii_tx = devm_clk_get(dev, "sgmii_tx"); ++ if (IS_ERR(mpcs->sgmii_tx)) ++ return PTR_ERR(mpcs->sgmii_tx); ++ ++ pcs = mtk_pcs_lynxi_init(dev, regmap, (uintptr_t)of_device_get_match_data(dev), ++ flags, mpcs); ++ if (IS_ERR(pcs)) ++ return PTR_ERR(pcs); ++ ++ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD); ++ ++ platform_set_drvdata(pdev, mpcs); ++ ++ mutex_lock(&instance_mutex); ++ list_add_tail(&mpcs->node, &mtk_pcs_lynxi_instances); ++ mutex_unlock(&instance_mutex); ++ ++ return 0; ++} ++ ++static void mtk_pcs_lynxi_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mtk_pcs_lynxi *cur, *tmp; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry_safe(cur, tmp, &mtk_pcs_lynxi_instances, node) ++ if (cur->dev == dev) { ++ list_del(&cur->node); ++ kfree(cur); ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++} ++ ++static const struct of_device_id mtk_pcs_lynxi_of_match[] = { ++ { .compatible = "mediatek,mt7988-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, mtk_pcs_lynxi_of_match); ++ ++struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np) ++{ ++ struct platform_device *pdev; ++ struct mtk_pcs_lynxi *mpcs; ++ ++ if (!np) ++ return NULL; ++ ++ if (!of_device_is_available(np)) ++ return ERR_PTR(-ENODEV); ++ ++ if (!of_match_node(mtk_pcs_lynxi_of_match, np)) ++ return ERR_PTR(-EINVAL); ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev || !platform_get_drvdata(pdev)) { ++ if (pdev) ++ put_device(&pdev->dev); ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ mpcs = platform_get_drvdata(pdev); ++ device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER); ++ ++ return &mpcs->pcs; ++} ++EXPORT_SYMBOL(mtk_pcs_lynxi_get); ++ ++void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) ++{ ++ struct mtk_pcs_lynxi *cur, *mpcs = NULL; ++ ++ if (!pcs) ++ return; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry(cur, &mtk_pcs_lynxi_instances, node) ++ if (pcs == &cur->pcs) { ++ mpcs = cur; ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++ ++ if (WARN_ON(!mpcs)) ++ return; ++ ++ put_device(mpcs->dev); ++} ++EXPORT_SYMBOL(mtk_pcs_lynxi_put); ++ ++static struct platform_driver mtk_pcs_lynxi_driver = { ++ .driver = { ++ .name = "mtk-pcs-lynxi", ++ .suppress_bind_attrs = true, ++ .of_match_table = mtk_pcs_lynxi_of_match, ++ }, ++ .probe = mtk_pcs_lynxi_probe, ++ .remove_new = mtk_pcs_lynxi_remove, ++}; ++module_platform_driver(mtk_pcs_lynxi_driver); ++ ++MODULE_AUTHOR("Daniel Golle "); + MODULE_DESCRIPTION("MediaTek SGMII library for LynxI"); + MODULE_LICENSE("GPL"); +--- a/include/linux/pcs/pcs-mtk-lynxi.h ++++ b/include/linux/pcs/pcs-mtk-lynxi.h +@@ -10,4 +10,15 @@ struct phylink_pcs *mtk_pcs_lynxi_create + struct regmap *regmap, + u32 ana_rgc3, u32 flags); + void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs); ++ ++#if IS_ENABLED(CONFIG_PCS_MTK_LYNXI) ++struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np); ++void mtk_pcs_lynxi_put(struct phylink_pcs *pcs); ++#else ++static inline struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np) ++{ ++ return NULL; ++} ++static inline void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) { } ++#endif /* IS_ENABLED(CONFIG_PCS_MTK_LYNXI) */ + #endif diff --git a/target/linux/generic/pending-6.12/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch b/target/linux/generic/pending-6.12/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch new file mode 100644 index 00000000000..215bd2ca2ef --- /dev/null +++ b/target/linux/generic/pending-6.12/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch @@ -0,0 +1,81 @@ +From 7d88d79c0f65b27a92754d7547f7af098b3de67b Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:47:31 +0000 +Subject: [PATCH 4/5] dt-bindings: net: pcs: add bindings for MediaTek USXGMII + PCS + +MediaTek's USXGMII can be found in the MT7988 SoC. We need to access +it in order to configure and monitor the Ethernet SerDes link in +USXGMII, 10GBase-R and 5GBase-R mode. By including a wrapped +legacy 1000Base-X/2500Base-X/Cisco SGMII LynxI PCS as well, those +interface modes are also available. + +Signed-off-by: Daniel Golle +--- + .../bindings/net/pcs/mediatek,usxgmii.yaml | 60 +++++++++++++++++++ + 1 file changed, 60 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/pcs/mediatek,usxgmii.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/pcs/mediatek,usxgmii.yaml +@@ -0,0 +1,60 @@ ++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/net/pcs/mediatek,usxgmii.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: MediaTek USXGMII PCS ++ ++maintainers: ++ - Daniel Golle ++ ++description: ++ The MediaTek USXGMII PCS provides physical link control and status ++ for USXGMII, 10GBase-R and 5GBase-R links on the SerDes interfaces ++ provided by the PEXTP PHY. ++ In order to also support legacy 2500Base-X, 1000Base-X and Cisco ++ SGMII an existing mediatek,*-sgmiisys LynxI PCS is wrapped to ++ provide those interfaces modes on the same SerDes interfaces shared ++ with the USXGMII PCS. ++ ++properties: ++ $nodename: ++ pattern: "^pcs@[0-9a-f]+$" ++ ++ compatible: ++ const: mediatek,mt7988-usxgmiisys ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: USXGMII top-level clock ++ ++ resets: ++ items: ++ - description: XFI reset ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - resets ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ #define MT7988_TOPRGU_XFI0_GRST 12 ++ soc { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ usxgmiisys0: pcs@10080000 { ++ compatible = "mediatek,mt7988-usxgmiisys"; ++ reg = <0 0x10080000 0 0x1000>; ++ clocks = <&topckgen CLK_TOP_USXGMII_SBUS_0_SEL>; ++ resets = <&watchdog MT7988_TOPRGU_XFI0_GRST>; ++ }; ++ }; diff --git a/target/linux/generic/pending-6.12/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch b/target/linux/generic/pending-6.12/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch new file mode 100644 index 00000000000..04790ce7e7f --- /dev/null +++ b/target/linux/generic/pending-6.12/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch @@ -0,0 +1,545 @@ +From dde0e95fff92e9f5009f3bea75278e0e34a48822 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:47:47 +0000 +Subject: [PATCH 5/5] net: pcs: add driver for MediaTek USXGMII PCS + +Add driver for USXGMII PCS found in the MediaTek MT7988 SoC and supporting +USXGMII, 10GBase-R and 5GBase-R interface modes. + +Signed-off-by: Daniel Golle +--- + MAINTAINERS | 2 + + drivers/net/pcs/Kconfig | 11 + + drivers/net/pcs/Makefile | 1 + + drivers/net/pcs/pcs-mtk-usxgmii.c | 456 ++++++++++++++++++++++++++++ + include/linux/pcs/pcs-mtk-usxgmii.h | 27 ++ + 5 files changed, 497 insertions(+) + create mode 100644 drivers/net/pcs/pcs-mtk-usxgmii.c + create mode 100644 include/linux/pcs/pcs-mtk-usxgmii.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -14432,7 +14432,9 @@ M: Daniel Golle + L: netdev@vger.kernel.org + S: Maintained + F: drivers/net/pcs/pcs-mtk-lynxi.c ++F: drivers/net/pcs/pcs-mtk-usxgmii.c + F: include/linux/pcs/pcs-mtk-lynxi.h ++F: include/linux/pcs/pcs-mtk-usxgmii.h + + MEDIATEK ETHERNET PHY DRIVERS + M: Daniel Golle +--- a/drivers/net/pcs/Kconfig ++++ b/drivers/net/pcs/Kconfig +@@ -25,6 +25,17 @@ config PCS_MTK_LYNXI + This module provides helpers to phylink for managing the LynxI PCS + which is part of MediaTek's SoC and Ethernet switch ICs. + ++config PCS_MTK_USXGMII ++ tristate "MediaTek USXGMII PCS" ++ select PCS_MTK_LYNXI ++ select PHY_MTK_PEXTP ++ select PHYLINK ++ help ++ This module provides a driver for MediaTek's USXGMII PCS supporting ++ 10GBase-R, 5GBase-R and USXGMII interface modes. ++ 1000Base-X, 2500Base-X and Cisco SGMII are supported on the same ++ differential pairs via an embedded LynxI PHY. ++ + config PCS_RZN1_MIIC + tristate "Renesas RZ/N1 MII converter" + depends on OF && (ARCH_RZN1 || COMPILE_TEST) +--- a/drivers/net/pcs/Makefile ++++ b/drivers/net/pcs/Makefile +@@ -8,3 +8,4 @@ obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o + obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o + obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o + obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o ++obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o +--- /dev/null ++++ b/drivers/net/pcs/pcs-mtk-usxgmii.c +@@ -0,0 +1,454 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2023 MediaTek Inc. ++ * Author: Henry Yen ++ * Daniel Golle ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* USXGMII subsystem config registers */ ++/* Register to control speed */ ++#define RG_PHY_TOP_SPEED_CTRL1 0x80c ++#define USXGMII_RATE_UPDATE_MODE BIT(31) ++#define USXGMII_MAC_CK_GATED BIT(29) ++#define USXGMII_IF_FORCE_EN BIT(28) ++#define USXGMII_RATE_ADAPT_MODE GENMASK(10, 8) ++#define USXGMII_RATE_ADAPT_MODE_X1 0 ++#define USXGMII_RATE_ADAPT_MODE_X2 1 ++#define USXGMII_RATE_ADAPT_MODE_X4 2 ++#define USXGMII_RATE_ADAPT_MODE_X10 3 ++#define USXGMII_RATE_ADAPT_MODE_X100 4 ++#define USXGMII_RATE_ADAPT_MODE_X5 5 ++#define USXGMII_RATE_ADAPT_MODE_X50 6 ++#define USXGMII_XFI_RX_MODE GENMASK(6, 4) ++#define USXGMII_XFI_TX_MODE GENMASK(2, 0) ++#define USXGMII_XFI_MODE_10G 0 ++#define USXGMII_XFI_MODE_5G 1 ++#define USXGMII_XFI_MODE_2P5G 3 ++ ++/* Register to control PCS AN */ ++#define RG_PCS_AN_CTRL0 0x810 ++#define USXGMII_AN_RESTART BIT(31) ++#define USXGMII_AN_SYNC_CNT GENMASK(30, 11) ++#define USXGMII_AN_ENABLE BIT(0) ++ ++#define RG_PCS_AN_CTRL2 0x818 ++#define USXGMII_LINK_TIMER_IDLE_DETECT GENMASK(29, 20) ++#define USXGMII_LINK_TIMER_COMP_ACK_DETECT GENMASK(19, 10) ++#define USXGMII_LINK_TIMER_AN_RESTART GENMASK(9, 0) ++ ++/* Register to read PCS AN status */ ++#define RG_PCS_AN_STS0 0x81c ++#define USXGMII_LPA GENMASK(15, 0) ++#define USXGMII_LPA_LATCH BIT(31) ++ ++/* Register to read PCS link status */ ++#define RG_PCS_RX_STATUS0 0x904 ++#define RG_PCS_RX_STATUS_UPDATE BIT(16) ++#define RG_PCS_RX_LINK_STATUS BIT(2) ++ ++/* struct mtk_usxgmii_pcs - This structure holds each usxgmii PCS ++ * @pcs: Phylink PCS structure ++ * @dev: Pointer to device structure ++ * @base: IO memory to access PCS hardware ++ * @clk: Pointer to USXGMII clk ++ * @reset: Pointer to USXGMII reset control ++ * @interface: Currently selected interface mode ++ * @neg_mode: Currently used phylink neg_mode ++ * @node: List node ++ */ ++struct mtk_usxgmii_pcs { ++ struct phylink_pcs pcs; ++ struct device *dev; ++ void __iomem *base; ++ struct clk *clk; ++ struct reset_control *reset; ++ phy_interface_t interface; ++ unsigned int neg_mode; ++ struct list_head node; ++}; ++ ++static LIST_HEAD(mtk_usxgmii_pcs_instances); ++static DEFINE_MUTEX(instance_mutex); ++ ++static u32 mtk_r32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg) ++{ ++ return ioread32(mpcs->base + reg); ++} ++ ++static void mtk_m32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg, u32 mask, u32 set) ++{ ++ u32 val; ++ ++ val = ioread32(mpcs->base + reg); ++ val &= ~mask; ++ val |= set; ++ iowrite32(val, mpcs->base + reg); ++} ++ ++static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mtk_usxgmii_pcs, pcs); ++} ++ ++static void mtk_usxgmii_reset(struct mtk_usxgmii_pcs *mpcs) ++{ ++ reset_control_assert(mpcs->reset); ++ udelay(100); ++ reset_control_deassert(mpcs->reset); ++ ++ mdelay(10); ++} ++ ++static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0; ++ bool mode_changed = false; ++ ++ if (interface == PHY_INTERFACE_MODE_USXGMII) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) | USXGMII_AN_ENABLE; ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_10G); ++ } else if (interface == PHY_INTERFACE_MODE_10GBASER) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_10G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else if (interface == PHY_INTERFACE_MODE_5GBASER) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0xFF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x3D); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_5G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_5G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else { ++ return -EINVAL; ++ } ++ ++ adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1); ++ ++ if (mpcs->interface != interface) { ++ mpcs->interface = interface; ++ mode_changed = true; ++ } ++ ++ mtk_usxgmii_reset(mpcs); ++ ++ /* Setup USXGMII AN ctrl */ ++ mtk_m32(mpcs, RG_PCS_AN_CTRL0, ++ USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE, ++ an_ctrl); ++ ++ mtk_m32(mpcs, RG_PCS_AN_CTRL2, ++ USXGMII_LINK_TIMER_IDLE_DETECT | ++ USXGMII_LINK_TIMER_COMP_ACK_DETECT | ++ USXGMII_LINK_TIMER_AN_RESTART, ++ link_timer); ++ ++ mpcs->neg_mode = neg_mode; ++ ++ /* Gated MAC CK */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_MAC_CK_GATED, USXGMII_MAC_CK_GATED); ++ ++ /* Enable interface force mode */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_IF_FORCE_EN, USXGMII_IF_FORCE_EN); ++ ++ /* Setup USXGMII adapt mode */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_RATE_UPDATE_MODE | USXGMII_RATE_ADAPT_MODE, ++ adapt_mode); ++ ++ /* Setup USXGMII speed */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE, ++ xfi_mode); ++ ++ usleep_range(1, 10); ++ ++ /* Un-gated MAC CK */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_MAC_CK_GATED, 0); ++ ++ usleep_range(1, 10); ++ ++ /* Disable interface force mode for the AN mode */ ++ if (an_ctrl & USXGMII_AN_ENABLE) ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_IF_FORCE_EN, 0); ++ ++ return mode_changed; ++} ++ ++static void mtk_usxgmii_pcs_get_fixed_speed(struct mtk_usxgmii_pcs *mpcs, ++ struct phylink_link_state *state) ++{ ++ u32 val = mtk_r32(mpcs, RG_PHY_TOP_SPEED_CTRL1); ++ int speed; ++ ++ /* Calculate speed from interface speed and rate adapt mode */ ++ switch (FIELD_GET(USXGMII_XFI_RX_MODE, val)) { ++ case USXGMII_XFI_MODE_10G: ++ speed = 10000; ++ break; ++ case USXGMII_XFI_MODE_5G: ++ speed = 5000; ++ break; ++ case USXGMII_XFI_MODE_2P5G: ++ speed = 2500; ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ return; ++ } ++ ++ switch (FIELD_GET(USXGMII_RATE_ADAPT_MODE, val)) { ++ case USXGMII_RATE_ADAPT_MODE_X100: ++ speed /= 100; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X50: ++ speed /= 50; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X10: ++ speed /= 10; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X5: ++ speed /= 5; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X4: ++ speed /= 4; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X2: ++ speed /= 2; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X1: ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ return; ++ } ++ ++ state->speed = speed; ++ state->duplex = DUPLEX_FULL; ++} ++ ++static void mtk_usxgmii_pcs_get_an_state(struct mtk_usxgmii_pcs *mpcs, ++ struct phylink_link_state *state) ++{ ++ u16 lpa; ++ ++ /* Refresh LPA by toggling LPA_LATCH */ ++ mtk_m32(mpcs, RG_PCS_AN_STS0, USXGMII_LPA_LATCH, USXGMII_LPA_LATCH); ++ ndelay(1020); ++ mtk_m32(mpcs, RG_PCS_AN_STS0, USXGMII_LPA_LATCH, 0); ++ ndelay(1020); ++ lpa = FIELD_GET(USXGMII_LPA, mtk_r32(mpcs, RG_PCS_AN_STS0)); ++ ++ phylink_decode_usxgmii_word(state, lpa); ++} ++ ++static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ ++ /* Refresh USXGMII link status by toggling RG_PCS_AN_STATUS_UPDATE */ ++ mtk_m32(mpcs, RG_PCS_RX_STATUS0, RG_PCS_RX_STATUS_UPDATE, ++ RG_PCS_RX_STATUS_UPDATE); ++ ndelay(1020); ++ mtk_m32(mpcs, RG_PCS_RX_STATUS0, RG_PCS_RX_STATUS_UPDATE, 0); ++ ndelay(1020); ++ ++ /* Read USXGMII link status */ ++ state->link = FIELD_GET(RG_PCS_RX_LINK_STATUS, ++ mtk_r32(mpcs, RG_PCS_RX_STATUS0)); ++ ++ /* Continuously repeat re-configuration sequence until link comes up */ ++ if (!state->link) { ++ mtk_usxgmii_pcs_config(pcs, mpcs->neg_mode, ++ state->interface, NULL, false); ++ return; ++ } ++ ++ if (FIELD_GET(USXGMII_AN_ENABLE, mtk_r32(mpcs, RG_PCS_AN_CTRL0))) ++ mtk_usxgmii_pcs_get_an_state(mpcs, state); ++ else ++ mtk_usxgmii_pcs_get_fixed_speed(mpcs, state); ++} ++ ++static void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ ++ mtk_m32(mpcs, RG_PCS_AN_CTRL0, USXGMII_AN_RESTART, USXGMII_AN_RESTART); ++} ++ ++static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, ++ phy_interface_t interface, ++ int speed, int duplex) ++{ ++ /* Reconfiguring USXGMII to ensure the quality of the RX signal ++ * after the line side link up. ++ */ ++ mtk_usxgmii_pcs_config(pcs, neg_mode, interface, NULL, false); ++} ++ ++static void mtk_usxgmii_pcs_disable(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mpcs->neg_mode = -1; ++} ++ ++static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = { ++ .pcs_config = mtk_usxgmii_pcs_config, ++ .pcs_get_state = mtk_usxgmii_pcs_get_state, ++ .pcs_an_restart = mtk_usxgmii_pcs_restart_an, ++ .pcs_link_up = mtk_usxgmii_pcs_link_up, ++ .pcs_disable = mtk_usxgmii_pcs_disable, ++}; ++ ++static int mtk_usxgmii_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mtk_usxgmii_pcs *mpcs; ++ ++ mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return -ENOMEM; ++ ++ mpcs->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mpcs->base)) ++ return PTR_ERR(mpcs->base); ++ ++ mpcs->dev = dev; ++ mpcs->pcs.ops = &mtk_usxgmii_pcs_ops; ++ mpcs->pcs.poll = true; ++ mpcs->pcs.neg_mode = true; ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mpcs->neg_mode = -1; ++ ++ mpcs->clk = devm_clk_get_enabled(mpcs->dev, NULL); ++ if (IS_ERR(mpcs->clk)) ++ return PTR_ERR(mpcs->clk); ++ ++ mpcs->reset = devm_reset_control_get_shared(dev, NULL); ++ if (IS_ERR(mpcs->reset)) ++ return PTR_ERR(mpcs->reset); ++ ++ reset_control_deassert(mpcs->reset); ++ ++ platform_set_drvdata(pdev, mpcs); ++ ++ mutex_lock(&instance_mutex); ++ list_add_tail(&mpcs->node, &mtk_usxgmii_pcs_instances); ++ mutex_unlock(&instance_mutex); ++ ++ return 0; ++} ++ ++static void mtk_usxgmii_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mtk_usxgmii_pcs *cur, *tmp; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry_safe(cur, tmp, &mtk_usxgmii_pcs_instances, node) ++ if (cur->dev == dev) { ++ list_del(&cur->node); ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++} ++ ++static const struct of_device_id mtk_usxgmii_of_mtable[] = { ++ { .compatible = "mediatek,mt7988-usxgmiisys" }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, mtk_usxgmii_of_mtable); ++ ++struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np) ++{ ++ struct platform_device *pdev; ++ struct mtk_usxgmii_pcs *mpcs; ++ ++ if (!np) ++ return NULL; ++ ++ if (!of_device_is_available(np)) ++ return ERR_PTR(-ENODEV); ++ ++ if (!of_match_node(mtk_usxgmii_of_mtable, np)) ++ return ERR_PTR(-EINVAL); ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev || !platform_get_drvdata(pdev)) { ++ if (pdev) ++ put_device(&pdev->dev); ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ mpcs = platform_get_drvdata(pdev); ++ device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER); ++ ++ return &mpcs->pcs; ++} ++EXPORT_SYMBOL(mtk_usxgmii_pcs_get); ++ ++void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *cur, *mpcs = NULL; ++ ++ if (!pcs) ++ return; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry(cur, &mtk_usxgmii_pcs_instances, node) ++ if (pcs == &cur->pcs) { ++ mpcs = cur; ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++ ++ if (WARN_ON(!mpcs)) ++ return; ++ ++ put_device(mpcs->dev); ++} ++EXPORT_SYMBOL(mtk_usxgmii_pcs_put); ++ ++static struct platform_driver mtk_usxgmii_driver = { ++ .driver = { ++ .name = "mtk_usxgmii", ++ .suppress_bind_attrs = true, ++ .of_match_table = mtk_usxgmii_of_mtable, ++ }, ++ .probe = mtk_usxgmii_probe, ++ .remove_new = mtk_usxgmii_remove, ++}; ++module_platform_driver(mtk_usxgmii_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MediaTek USXGMII PCS driver"); ++MODULE_AUTHOR("Daniel Golle "); +--- /dev/null ++++ b/include/linux/pcs/pcs-mtk-usxgmii.h +@@ -0,0 +1,27 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __LINUX_PCS_MTK_USXGMII_H ++#define __LINUX_PCS_MTK_USXGMII_H ++ ++#include ++ ++/** ++ * mtk_usxgmii_select_pcs() - Get MediaTek PCS instance ++ * @np: Pointer to device node indentifying a MediaTek USXGMII PCS ++ * @mode: Ethernet PHY interface mode ++ * ++ * Return PCS identified by a device node and the PHY interface mode in use ++ * ++ * Return: Pointer to phylink PCS instance of NULL ++ */ ++#if IS_ENABLED(CONFIG_PCS_MTK_USXGMII) ++struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np); ++void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs); ++#else ++static inline struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np) ++{ ++ return NULL; ++} ++static inline void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs) { } ++#endif /* IS_ENABLED(CONFIG_PCS_MTK_USXGMII) */ ++ ++#endif /* __LINUX_PCS_MTK_USXGMII_H */ diff --git a/target/linux/generic/pending-6.12/740-net-phy-motorcomm-Add-missing-include.patch b/target/linux/generic/pending-6.12/740-net-phy-motorcomm-Add-missing-include.patch new file mode 100644 index 00000000000..2a1f908cfb3 --- /dev/null +++ b/target/linux/generic/pending-6.12/740-net-phy-motorcomm-Add-missing-include.patch @@ -0,0 +1,22 @@ +From 6f291aa7da199c6486cc229b055dcbcd5cee7a21 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Sun, 21 May 2023 22:24:56 +0200 +Subject: [PATCH] net: phy: motorcomm: Add missing include + +Directly include linux/bitfield.h which provides FIELD_PREP. + +Signed-off-by: Hauke Mehrtens +--- + drivers/net/phy/motorcomm.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/phy/motorcomm.c ++++ b/drivers/net/phy/motorcomm.c +@@ -6,6 +6,7 @@ + * Author: Frank + */ + ++#include + #include + #include + #include diff --git a/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch b/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch new file mode 100644 index 00000000000..7a52bd55213 --- /dev/null +++ b/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch @@ -0,0 +1,35 @@ +From 1be3688b3eaa7ea2d9e19bd29ae6a6a51c121a0b Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Sat, 16 Nov 2024 22:36:15 +0100 +Subject: [PATCH] net: phy: broadcom: update dependency condition + +The broadcom PHY driver only has to depend upon PTP_1588_CLOCK_OPTIONAL +if NETWORK_PHY_TIMESTAMPING is enabled. The PTP functionality is stubbed +in this case. + +Reflect this circumstance in the dependence condition. This allows to +build the driver as a built-in module even if PTP is built as a module. + +This is required to include the broadcom PHY module regardless of the +built-setting of the PTP subsystem. On ath79 (and probably more) +targets with Broadcom PHY, Gigabit operation is currently broken as the +PHY driver is only built as a module in case all kernel-packages are +built. Due to this circumstance, affected devices fall back to using the +generic PHY driver. + +Signed-off-by: David Bauer +--- + drivers/net/phy/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -151,7 +151,7 @@ config BROADCOM_PHY + tristate "Broadcom 54XX PHYs" + select BCM_NET_PHYLIB + select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING +- depends on PTP_1588_CLOCK_OPTIONAL ++ depends on NETWORK_PHY_TIMESTAMPING=n || PTP_1588_CLOCK_OPTIONAL + help + Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464, + BCM5481, BCM54810 and BCM5482 PHYs. diff --git a/target/linux/generic/pending-6.12/750-net-sfp-add-quirk-for-QINIYEK-BJ-SFP-10G-T-copper-SF.patch b/target/linux/generic/pending-6.12/750-net-sfp-add-quirk-for-QINIYEK-BJ-SFP-10G-T-copper-SF.patch new file mode 100644 index 00000000000..fc2d26cb1c0 --- /dev/null +++ b/target/linux/generic/pending-6.12/750-net-sfp-add-quirk-for-QINIYEK-BJ-SFP-10G-T-copper-SF.patch @@ -0,0 +1,26 @@ +From 51a16863b04b1c2b28d45d80934f2267547734b7 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 9 May 2025 16:33:27 +0100 +Subject: [PATCH] net: sfp: add quirk for QINIYEK BJ-SFP-10G-T copper SFP+ + module + +Add quirk for a copper SFP+ module that identifies itself as "QINIYEK" +"BJ-SFP-10G-T". + +It uses RollBall protocol to talk to the built-in RealTek RTL8261N PHY. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/sfp.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -557,6 +557,7 @@ static const struct sfp_quirk sfp_quirks + SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-U", sfp_quirk_2500basex), + SFP_QUIRK_F("OEM", "RTSFP-10", sfp_fixup_rollball_cc), + SFP_QUIRK_F("OEM", "RTSFP-10G", sfp_fixup_rollball_cc), ++ SFP_QUIRK_F("QINIYEK", "BJ-SFP-10G-T", sfp_fixup_fs_10gt), + SFP_QUIRK_F("Turris", "RTSFP-2.5G", sfp_fixup_rollball), + SFP_QUIRK_F("Turris", "RTSFP-10", sfp_fixup_rollball), + SFP_QUIRK_F("Turris", "RTSFP-10G", sfp_fixup_rollball), diff --git a/target/linux/generic/pending-6.12/760-01-net-dsa-move-dsa_bridge_ports-helper-to-dsa.h.patch b/target/linux/generic/pending-6.12/760-01-net-dsa-move-dsa_bridge_ports-helper-to-dsa.h.patch new file mode 100644 index 00000000000..c0e77d17152 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-01-net-dsa-move-dsa_bridge_ports-helper-to-dsa.h.patch @@ -0,0 +1,38 @@ +From de6dd19a3edd1dc6400fecf77610e438441a02ac Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 25 Mar 2026 17:54:11 +0000 +Subject: [PATCH 10/35] net: dsa: move dsa_bridge_ports() helper to dsa.h + +The yt921x driver contains a helper to create a bitmap of ports +which are members of a bridge. + +Move the helper as static inline function into dsa.h, so other driver +can make use of it as well. + +Signed-off-by: Daniel Golle +--- + include/net/dsa.h | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -832,6 +832,19 @@ dsa_tree_offloads_bridge_dev(struct dsa_ + return false; + } + ++static inline u32 ++dsa_bridge_ports(struct dsa_switch *ds, const struct net_device *bdev) ++{ ++ struct dsa_port *dp; ++ u32 mask = 0; ++ ++ dsa_switch_for_each_user_port(dp, ds) ++ if (dsa_port_offloads_bridge_dev(dp, bdev)) ++ mask |= BIT(dp->index); ++ ++ return mask; ++} ++ + static inline bool dsa_port_tree_same(const struct dsa_port *a, + const struct dsa_port *b) + { diff --git a/target/linux/generic/pending-6.12/760-02-net-dsa-add-bridge-member-iteration-macro.patch b/target/linux/generic/pending-6.12/760-02-net-dsa-add-bridge-member-iteration-macro.patch new file mode 100644 index 00000000000..37891ffe39a --- /dev/null +++ b/target/linux/generic/pending-6.12/760-02-net-dsa-add-bridge-member-iteration-macro.patch @@ -0,0 +1,45 @@ +From 880cde7abf58cb1316382ae7f59aac93c313e8fe Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 25 Mar 2026 17:54:41 +0000 +Subject: [PATCH 11/35] net: dsa: add bridge member iteration macro + +Drivers that offload bridges need to iterate over the ports that are +members of a given bridge, for example to rebuild per-port forwarding +bitmaps when membership changes. Currently drivers typically open-code +this by combining dsa_switch_for_each_user_port() with a +dsa_port_offloads_bridge_dev() check, or cache bridge membership +within the driver. + +Add dsa_switch_for_each_bridge_member() macro to express this pattern +directly, and use it for the existing dsa_bridge_ports() inline +helper. + +Signed-off-by: Daniel Golle +--- + include/net/dsa.h | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -832,15 +832,18 @@ dsa_tree_offloads_bridge_dev(struct dsa_ + return false; + } + ++#define dsa_switch_for_each_bridge_member(_dp, _ds, _bdev) \ ++ dsa_switch_for_each_user_port(_dp, _ds) \ ++ if (dsa_port_offloads_bridge_dev(_dp, _bdev)) ++ + static inline u32 + dsa_bridge_ports(struct dsa_switch *ds, const struct net_device *bdev) + { + struct dsa_port *dp; + u32 mask = 0; + +- dsa_switch_for_each_user_port(dp, ds) +- if (dsa_port_offloads_bridge_dev(dp, bdev)) +- mask |= BIT(dp->index); ++ dsa_switch_for_each_bridge_member(dp, ds, bdev) ++ mask |= BIT(dp->index); + + return mask; + } diff --git a/target/linux/generic/pending-6.12/760-03-dsa-tag_mxl862xx-set-dsa_default_offload_fwd_mark.patch b/target/linux/generic/pending-6.12/760-03-dsa-tag_mxl862xx-set-dsa_default_offload_fwd_mark.patch new file mode 100644 index 00000000000..7c824994aa0 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-03-dsa-tag_mxl862xx-set-dsa_default_offload_fwd_mark.patch @@ -0,0 +1,29 @@ +From 149bb02d5bf031a1eb85f91377f54913de3a08ff Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 25 Mar 2026 17:54:52 +0000 +Subject: [PATCH 12/35] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark() + +The MxL862xx offloads bridge forwarding in hardware, so set +dsa_default_offload_fwd_mark() to avoid duplicate forwarding of +packets of (eg. flooded) frames arriving at the CPU port. + +Link-local frames are directly trapped to the CPU port only, so don't +set dsa_default_offload_fwd_mark() on those. + +Signed-off-by: Daniel Golle +--- + net/dsa/tag_mxl862xx.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/dsa/tag_mxl862xx.c ++++ b/net/dsa/tag_mxl862xx.c +@@ -86,6 +86,9 @@ static struct sk_buff *mxl862_tag_rcv(st + return NULL; + } + ++ if (likely(!is_link_local_ether_addr(eth_hdr(skb)->h_dest))) ++ dsa_default_offload_fwd_mark(skb); ++ + /* remove the MxL862xx special tag between the MAC addresses and the + * current ethertype field. + */ diff --git a/target/linux/generic/pending-6.12/760-04-net-dsa-mxl862xx-implement-bridge-offloading.patch b/target/linux/generic/pending-6.12/760-04-net-dsa-mxl862xx-implement-bridge-offloading.patch new file mode 100644 index 00000000000..9d1c1c958b8 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-04-net-dsa-mxl862xx-implement-bridge-offloading.patch @@ -0,0 +1,1382 @@ +From 5acdee6df2fbd4a9b02045694227f25cb1d4e5e0 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 25 Mar 2026 17:55:08 +0000 +Subject: [PATCH 13/35] net: dsa: mxl862xx: implement bridge offloading + +Implement joining and leaving bridges as well as add, delete and dump +operations on isolated FDBs, port MDB membership management, and +setting a port's STP state. + +The switch supports a maximum of 63 bridges, however, up to 12 may +be used as "single-port bridges" to isolate standalone ports. +Allowing up to 48 bridges to be offloaded seems more than enough on +that hardware, hence that is set as max_num_bridges. + +A total of 128 bridge ports are supported in the bridge portmap, and +virtual bridge ports have to be used eg. for link-aggregation, hence +potentially exceeding the number of hardware ports. + +The firmware-assigned bridge identifier (FID) for each offloaded bridge +is stored in an array used to map DSA bridge num to firmware bridge ID, +avoiding the need for a driver-private bridge tracking structure. +Bridge member portmaps are rebuilt on join/leave using +dsa_switch_for_each_bridge_member(). + +As there are now more users of the BRIDGEPORT_CONFIG_SET API and the +state of each port is cached locally, introduce a helper function +mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) which is +then used to replace the direct calls to the API in +mxl862xx_setup_cpu_bridge() and mxl862xx_add_single_port_bridge(). + +Note that there is no convenient way to control flooding on per-port +level, so the driver is using a 0-rate QoS meter setup as a stopper in +lack of any better option. In order to be perfect the firmware-enforced +minimum bucket size is bypassed by directly writing 0s to the relevant +registers -- without that at least one 64-byte packet could still +pass before the meter would change from 'yellow' into 'red' state. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 225 ++++++- + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 20 +- + drivers/net/dsa/mxl862xx/mxl862xx.c | 752 ++++++++++++++++++++++-- + drivers/net/dsa/mxl862xx/mxl862xx.h | 133 +++++ + 4 files changed, 1087 insertions(+), 43 deletions(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -3,6 +3,7 @@ + #ifndef __MXL862XX_API_H + #define __MXL862XX_API_H + ++#include + #include + + /** +@@ -35,6 +36,168 @@ struct mxl862xx_register_mod { + } __packed; + + /** ++ * enum mxl862xx_mac_table_filter - Source/Destination MAC address filtering ++ * ++ * @MXL862XX_MAC_FILTER_NONE: no filter ++ * @MXL862XX_MAC_FILTER_SRC: source address filter ++ * @MXL862XX_MAC_FILTER_DEST: destination address filter ++ * @MXL862XX_MAC_FILTER_BOTH: both source and destination filter ++ */ ++enum mxl862xx_mac_table_filter { ++ MXL862XX_MAC_FILTER_NONE = 0, ++ MXL862XX_MAC_FILTER_SRC = BIT(0), ++ MXL862XX_MAC_FILTER_DEST = BIT(1), ++ MXL862XX_MAC_FILTER_BOTH = BIT(0) | BIT(1), ++}; ++ ++#define MXL862XX_TCI_VLAN_ID GENMASK(11, 0) ++#define MXL862XX_TCI_VLAN_CFI_DEI BIT(12) ++#define MXL862XX_TCI_VLAN_PRI GENMASK(15, 13) ++ ++/* Set in port_id to use port_map[] as a portmap bitmap instead of a single ++ * port ID. When clear, port_id selects one port; when set, the firmware ++ * ignores the lower bits of port_id and writes port_map[] directly into ++ * the PCE bridge port map. ++ */ ++#define MXL862XX_PORTMAP_FLAG BIT(31) ++ ++/** ++ * struct mxl862xx_mac_table_add - MAC Table Entry to be added ++ * @fid: Filtering Identifier (FID) (not supported by all switches) ++ * @port_id: Ethernet Port number ++ * @port_map: Bridge Port Map ++ * @sub_if_id: Sub-Interface Identifier Destination ++ * @age_timer: Aging Time in seconds ++ * @vlan_id: STAG VLAN Id ++ * @static_entry: Static Entry (value will be aged out if not set to static) ++ * @traffic_class: Egress queue traffic class ++ * @mac: MAC Address to add to the table ++ * @filter_flag: See &enum mxl862xx_mac_table_filter ++ * @igmp_controlled: Packet is marked as IGMP controlled if destination MAC ++ * address matches MAC in this entry ++ * @associated_mac: Associated Mac address ++ * @tci: TCI for B-Step ++ * Bit [0:11] - VLAN ID ++ * Bit [12] - VLAN CFI/DEI ++ * Bit [13:15] - VLAN PRI ++ */ ++struct mxl862xx_mac_table_add { ++ __le16 fid; ++ __le32 port_id; ++ __le16 port_map[8]; ++ __le16 sub_if_id; ++ __le32 age_timer; ++ __le16 vlan_id; ++ u8 static_entry; ++ u8 traffic_class; ++ u8 mac[ETH_ALEN]; ++ u8 filter_flag; ++ u8 igmp_controlled; ++ u8 associated_mac[ETH_ALEN]; ++ __le16 tci; ++} __packed; ++ ++/** ++ * struct mxl862xx_mac_table_remove - MAC Table Entry to be removed ++ * @fid: Filtering Identifier (FID) ++ * @mac: MAC Address to be removed from the table. ++ * @filter_flag: See &enum mxl862xx_mac_table_filter ++ * @tci: TCI for B-Step ++ * Bit [0:11] - VLAN ID ++ * Bit [12] - VLAN CFI/DEI ++ * Bit [13:15] - VLAN PRI ++ */ ++struct mxl862xx_mac_table_remove { ++ __le16 fid; ++ u8 mac[ETH_ALEN]; ++ u8 filter_flag; ++ __le16 tci; ++} __packed; ++ ++/** ++ * struct mxl862xx_mac_table_read - MAC Table Entry to be read ++ * @initial: Restart the get operation from the beginning of the table ++ * @last: Indicates that the read operation returned last entry ++ * @fid: Get the MAC table entry belonging to the given Filtering Identifier ++ * @port_id: The Bridge Port ID ++ * @port_map: Bridge Port Map ++ * @age_timer: Aging Time ++ * @vlan_id: STAG VLAN Id ++ * @static_entry: Indicates if this is a Static Entry ++ * @sub_if_id: Sub-Interface Identifier Destination ++ * @mac: MAC Address. Filled out by the switch API implementation. ++ * @filter_flag: See &enum mxl862xx_mac_table_filter ++ * @igmp_controlled: Packet is marked as IGMP controlled if destination MAC ++ * address matches the MAC in this entry ++ * @entry_changed: Indicate if the Entry has Changed ++ * @associated_mac: Associated MAC address ++ * @hit_status: MAC Table Hit Status Update ++ * @tci: TCI for B-Step ++ * Bit [0:11] - VLAN ID ++ * Bit [12] - VLAN CFI/DEI ++ * Bit [13:15] - VLAN PRI ++ * @first_bridge_port_id: The port this MAC address has first been learned. ++ * This is used for loop detection. ++ */ ++struct mxl862xx_mac_table_read { ++ u8 initial; ++ u8 last; ++ __le16 fid; ++ __le32 port_id; ++ __le16 port_map[8]; ++ __le32 age_timer; ++ __le16 vlan_id; ++ u8 static_entry; ++ __le16 sub_if_id; ++ u8 mac[ETH_ALEN]; ++ u8 filter_flag; ++ u8 igmp_controlled; ++ u8 entry_changed; ++ u8 associated_mac[ETH_ALEN]; ++ u8 hit_status; ++ __le16 tci; ++ __le16 first_bridge_port_id; ++} __packed; ++ ++/** ++ * struct mxl862xx_mac_table_query - MAC Table Entry key-based lookup ++ * @mac: MAC Address to search for (input) ++ * @fid: Filtering Identifier (input) ++ * @found: Set by firmware: 1 if entry was found, 0 if not ++ * @port_id: Bridge Port ID (output; MSB set if portmap mode) ++ * @port_map: Bridge Port Map (output; valid for static entries) ++ * @sub_if_id: Sub-Interface Identifier Destination ++ * @age_timer: Aging Time ++ * @vlan_id: STAG VLAN Id ++ * @static_entry: Indicates if this is a Static Entry ++ * @filter_flag: See &enum mxl862xx_mac_table_filter (input+output) ++ * @igmp_controlled: IGMP controlled flag ++ * @entry_changed: Entry changed flag ++ * @associated_mac: Associated MAC address ++ * @hit_status: MAC Table Hit Status Update ++ * @tci: TCI (VLAN ID + CFI/DEI + PRI) (input) ++ * @first_bridge_port_id: First learned bridge port ++ */ ++struct mxl862xx_mac_table_query { ++ u8 mac[ETH_ALEN]; ++ __le16 fid; ++ u8 found; ++ __le32 port_id; ++ __le16 port_map[8]; ++ __le16 sub_if_id; ++ __le32 age_timer; ++ __le16 vlan_id; ++ u8 static_entry; ++ u8 filter_flag; ++ u8 igmp_controlled; ++ u8 entry_changed; ++ u8 associated_mac[ETH_ALEN]; ++ u8 hit_status; ++ __le16 tci; ++ __le16 first_bridge_port_id; ++} __packed; ++ ++/** + * enum mxl862xx_mac_clear_type - MAC table clear type + * @MXL862XX_MAC_CLEAR_PHY_PORT: clear dynamic entries based on port_id + * @MXL862XX_MAC_CLEAR_DYNAMIC: clear all dynamic entries +@@ -139,6 +302,40 @@ enum mxl862xx_bridge_port_egress_meter { + }; + + /** ++ * struct mxl862xx_qos_meter_cfg - Rate meter configuration ++ * @enable: Enable/disable meter ++ * @meter_id: Meter ID (assigned by firmware on alloc) ++ * @meter_name: Meter name string ++ * @meter_type: Meter algorithm type (srTCM = 0, trTCM = 1) ++ * @cbs: Committed Burst Size (in bytes) ++ * @res1: Reserved ++ * @ebs: Excess Burst Size (in bytes) ++ * @res2: Reserved ++ * @rate: Committed Information Rate (in kbit/s) ++ * @pi_rate: Peak Information Rate (in kbit/s) ++ * @colour_blind_mode: Colour-blind mode enable ++ * @pkt_mode: Packet mode enable ++ * @local_overhd: Local overhead accounting enable ++ * @local_overhd_val: Local overhead accounting value ++ */ ++struct mxl862xx_qos_meter_cfg { ++ u8 enable; ++ __le16 meter_id; ++ char meter_name[32]; ++ __le32 meter_type; ++ __le32 cbs; ++ __le32 res1; ++ __le32 ebs; ++ __le32 res2; ++ __le32 rate; ++ __le32 pi_rate; ++ u8 colour_blind_mode; ++ u8 pkt_mode; ++ u8 local_overhd; ++ __le16 local_overhd_val; ++} __packed; ++ ++/** + * enum mxl862xx_bridge_forward_mode - Bridge forwarding type of packet + * @MXL862XX_BRIDGE_FORWARD_FLOOD: Packet is flooded to port members of + * ingress bridge port +@@ -456,7 +653,7 @@ struct mxl862xx_pmapper { + */ + struct mxl862xx_bridge_port_config { + __le16 bridge_port_id; +- __le32 mask; /* enum mxl862xx_bridge_port_config_mask */ ++ __le32 mask; /* enum mxl862xx_bridge_port_config_mask */ + __le16 bridge_id; + u8 ingress_extended_vlan_enable; + __le16 ingress_extended_vlan_block_id; +@@ -659,6 +856,32 @@ struct mxl862xx_ctp_port_assignment { + } __packed; + + /** ++ * enum mxl862xx_stp_port_state - Spanning Tree Protocol port states ++ * @MXL862XX_STP_PORT_STATE_FORWARD: Forwarding state ++ * @MXL862XX_STP_PORT_STATE_DISABLE: Disabled/Discarding state ++ * @MXL862XX_STP_PORT_STATE_LEARNING: Learning state ++ * @MXL862XX_STP_PORT_STATE_BLOCKING: Blocking/Listening ++ */ ++enum mxl862xx_stp_port_state { ++ MXL862XX_STP_PORT_STATE_FORWARD = 0, ++ MXL862XX_STP_PORT_STATE_DISABLE, ++ MXL862XX_STP_PORT_STATE_LEARNING, ++ MXL862XX_STP_PORT_STATE_BLOCKING, ++}; ++ ++/** ++ * struct mxl862xx_stp_port_cfg - Configures the Spanning Tree Protocol state ++ * @port_id: Port number ++ * @fid: Filtering Identifier (FID) ++ * @port_state: See &enum mxl862xx_stp_port_state ++ */ ++struct mxl862xx_stp_port_cfg { ++ __le16 port_id; ++ __le16 fid; ++ __le32 port_state; /* enum mxl862xx_stp_port_state */ ++} __packed; ++ ++/** + * struct mxl862xx_sys_fw_image_version - Firmware version information + * @iv_major: firmware major version + * @iv_minor: firmware minor version +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -15,12 +15,15 @@ + #define MXL862XX_BRDG_MAGIC 0x300 + #define MXL862XX_BRDGPORT_MAGIC 0x400 + #define MXL862XX_CTP_MAGIC 0x500 ++#define MXL862XX_QOS_MAGIC 0x600 + #define MXL862XX_SWMAC_MAGIC 0xa00 ++#define MXL862XX_STP_MAGIC 0xf00 + #define MXL862XX_SS_MAGIC 0x1600 + #define GPY_GPY2XX_MAGIC 0x1800 + #define SYS_MISC_MAGIC 0x1900 + + #define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9) ++#define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa) + #define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11) + + #define MXL862XX_BRIDGE_ALLOC (MXL862XX_BRDG_MAGIC + 0x1) +@@ -35,14 +38,23 @@ + + #define MXL862XX_CTP_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3) + ++#define MXL862XX_QOS_METERCFGSET (MXL862XX_QOS_MAGIC + 0x2) ++#define MXL862XX_QOS_METERALLOC (MXL862XX_QOS_MAGIC + 0x2a) ++ ++#define MXL862XX_MAC_TABLEENTRYADD (MXL862XX_SWMAC_MAGIC + 0x2) ++#define MXL862XX_MAC_TABLEENTRYREAD (MXL862XX_SWMAC_MAGIC + 0x3) ++#define MXL862XX_MAC_TABLEENTRYQUERY (MXL862XX_SWMAC_MAGIC + 0x4) ++#define MXL862XX_MAC_TABLEENTRYREMOVE (MXL862XX_SWMAC_MAGIC + 0x5) + #define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8) + +-#define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x02) ++#define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x2) ++ ++#define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2) + +-#define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x01) +-#define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x02) ++#define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x1) ++#define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x2) + +-#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x02) ++#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2) + + #define MMD_API_MAXIMUM_ID 0x7fff + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -7,8 +7,11 @@ + * Copyright (C) 2025 Daniel Golle + */ + +-#include ++#include + #include ++#include ++#include ++#include + #include + #include + #include +@@ -36,6 +39,17 @@ + #define MXL862XX_READY_TIMEOUT_MS 10000 + #define MXL862XX_READY_POLL_MS 100 + ++#define MXL862XX_TCM_INST_SEL 0xe00 ++#define MXL862XX_TCM_CBS 0xe12 ++#define MXL862XX_TCM_EBS 0xe13 ++ ++static const int mxl862xx_flood_meters[] = { ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC, ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP, ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP, ++ MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST, ++}; ++ + static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol m) +@@ -168,6 +182,225 @@ static int mxl862xx_setup_mdio(struct ds + return ret; + } + ++static int mxl862xx_bridge_config_fwd(struct dsa_switch *ds, u16 bridge_id, ++ bool ucast_flood, bool mcast_flood, ++ bool bcast_flood) ++{ ++ struct mxl862xx_bridge_config bridge_config = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ int ret; ++ ++ bridge_config.mask = cpu_to_le32(MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE); ++ bridge_config.bridge_id = cpu_to_le16(bridge_id); ++ ++ bridge_config.forward_unknown_unicast = cpu_to_le32(ucast_flood ? ++ MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD); ++ ++ bridge_config.forward_unknown_multicast_ip = cpu_to_le32(mcast_flood ? ++ MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD); ++ bridge_config.forward_unknown_multicast_non_ip = ++ bridge_config.forward_unknown_multicast_ip; ++ ++ bridge_config.forward_broadcast = cpu_to_le32(bcast_flood ? ++ MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD); ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_BRIDGE_CONFIGSET, bridge_config); ++ if (ret) ++ dev_err(ds->dev, "failed to configure bridge %u forwarding: %d\n", ++ bridge_id, ret); ++ ++ return ret; ++} ++ ++/* Allocate a single zero-rate meter shared by all ports and flood types. ++ * All flood-blocking egress sub-meters point to this one meter so that any ++ * packet hitting this meter is unconditionally dropped. ++ * ++ * The firmware API requires CBS >= 64 (its bs2ls encoder clamps smaller ++ * values), so the meter is initially configured with CBS=EBS=64. ++ * A zero-rate bucket starts full at CBS bytes, which would let one packet ++ * through before the bucket empties. To eliminate this one-packet leak we ++ * override CBS and EBS to zero via direct register writes after the API call; ++ * the hardware accepts CBS=0 and immediately flags the bucket as exceeded, ++ * so no traffic can ever pass. ++ */ ++static int mxl862xx_setup_drop_meter(struct dsa_switch *ds) ++{ ++ struct mxl862xx_qos_meter_cfg meter = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_register_mod reg; ++ int ret; ++ ++ /* meter_id=0 means auto-alloc */ ++ ret = MXL862XX_API_READ(priv, MXL862XX_QOS_METERALLOC, meter); ++ if (ret) ++ return ret; ++ ++ meter.enable = true; ++ meter.cbs = cpu_to_le32(64); ++ meter.ebs = cpu_to_le32(64); ++ snprintf(meter.meter_name, sizeof(meter.meter_name), "drop"); ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_QOS_METERCFGSET, meter); ++ if (ret) ++ return ret; ++ ++ priv->drop_meter = le16_to_cpu(meter.meter_id); ++ ++ /* Select the meter instance for subsequent TCM register access. */ ++ reg.addr = cpu_to_le16(MXL862XX_TCM_INST_SEL); ++ reg.data = cpu_to_le16(priv->drop_meter); ++ reg.mask = cpu_to_le16(0xffff); ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg); ++ if (ret) ++ return ret; ++ ++ /* Zero CBS so the committed bucket starts empty (exceeded). */ ++ reg.addr = cpu_to_le16(MXL862XX_TCM_CBS); ++ reg.data = 0; ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg); ++ if (ret) ++ return ret; ++ ++ /* Zero EBS so the excess bucket starts empty (exceeded). */ ++ reg.addr = cpu_to_le16(MXL862XX_TCM_EBS); ++ return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg); ++} ++ ++static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_bridge_port_config br_port_cfg = {}; ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ u16 bridge_id = dp->bridge ? ++ priv->bridges[dp->bridge->num] : p->fid; ++ bool enable; ++ int i, idx; ++ ++ br_port_cfg.bridge_port_id = cpu_to_le16(port); ++ br_port_cfg.bridge_id = cpu_to_le16(bridge_id); ++ br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER); ++ br_port_cfg.src_mac_learning_disable = !p->learning; ++ ++ mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap); ++ ++ for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) { ++ idx = mxl862xx_flood_meters[i]; ++ enable = !!(p->flood_block & BIT(idx)); ++ ++ br_port_cfg.egress_traffic_sub_meter_id[idx] = ++ enable ? cpu_to_le16(priv->drop_meter) : 0; ++ br_port_cfg.egress_sub_metering_enable[idx] = enable; ++ } ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, ++ br_port_cfg); ++} ++ ++static int mxl862xx_sync_bridge_members(struct dsa_switch *ds, ++ const struct dsa_bridge *bridge) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct dsa_port *dp, *member_dp; ++ int port, err, ret = 0; ++ ++ dsa_switch_for_each_bridge_member(dp, ds, bridge->dev) { ++ port = dp->index; ++ ++ bitmap_zero(priv->ports[port].portmap, ++ MXL862XX_MAX_BRIDGE_PORTS); ++ ++ dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev) { ++ if (member_dp->index != port) ++ __set_bit(member_dp->index, ++ priv->ports[port].portmap); ++ } ++ __set_bit(dp->cpu_dp->index, priv->ports[port].portmap); ++ ++ err = mxl862xx_set_bridge_port(ds, port); ++ if (err) ++ ret = err; ++ } ++ ++ return ret; ++} ++ ++/** ++ * mxl862xx_allocate_bridge - Allocate a firmware bridge instance ++ * @priv: driver private data ++ * @bridge_id: output -- firmware bridge ID assigned by the firmware ++ * ++ * Newly allocated bridges default to flooding all traffic classes ++ * (unknown unicast, multicast, broadcast). Callers that need ++ * different forwarding behavior must call mxl862xx_bridge_config_fwd() ++ * after allocation. ++ * ++ * Return: 0 on success, negative errno on failure. ++ */ ++static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id) ++{ ++ struct mxl862xx_bridge_alloc br_alloc = {}; ++ int ret; ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_BRIDGE_ALLOC, br_alloc); ++ if (ret) ++ return ret; ++ ++ *bridge_id = le16_to_cpu(br_alloc.bridge_id); ++ return 0; ++} ++ ++static void mxl862xx_free_bridge(struct dsa_switch *ds, ++ const struct dsa_bridge *bridge) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ u16 fw_id = priv->bridges[bridge->num]; ++ struct mxl862xx_bridge_alloc br_alloc = { ++ .bridge_id = cpu_to_le16(fw_id), ++ }; ++ int ret; ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_BRIDGE_FREE, br_alloc); ++ if (ret) { ++ dev_err(ds->dev, "failed to free fw bridge %u: %pe\n", ++ fw_id, ERR_PTR(ret)); ++ return; ++ } ++ ++ priv->bridges[bridge->num] = 0; ++} ++ ++static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port) ++{ ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct mxl862xx_priv *priv = ds->priv; ++ int ret; ++ ++ ret = mxl862xx_allocate_bridge(priv, &priv->ports[port].fid); ++ if (ret) { ++ dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port); ++ return ret; ++ } ++ ++ priv->ports[port].learning = false; ++ bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS); ++ __set_bit(dp->cpu_dp->index, priv->ports[port].portmap); ++ ++ ret = mxl862xx_set_bridge_port(ds, port); ++ if (ret) ++ return ret; ++ ++ /* Standalone ports should not flood unknown unicast or multicast ++ * towards the CPU by default; only broadcast is needed initially. ++ */ ++ return mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid, ++ false, false, true); ++} ++ + static int mxl862xx_setup(struct dsa_switch *ds) + { + struct mxl862xx_priv *priv = ds->priv; +@@ -181,6 +414,10 @@ static int mxl862xx_setup(struct dsa_swi + if (ret) + return ret; + ++ ret = mxl862xx_setup_drop_meter(ds); ++ if (ret) ++ return ret; ++ + return mxl862xx_setup_mdio(ds); + } + +@@ -260,66 +497,87 @@ static int mxl862xx_configure_sp_tag_pro + + static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port) + { +- struct mxl862xx_bridge_port_config br_port_cfg = {}; + struct mxl862xx_priv *priv = ds->priv; +- u16 bridge_port_map = 0; + struct dsa_port *dp; + +- /* CPU port bridge setup */ +- br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | +- MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | +- MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); +- +- br_port_cfg.bridge_port_id = cpu_to_le16(port); +- br_port_cfg.src_mac_learning_disable = false; +- br_port_cfg.vlan_src_mac_vid_enable = true; +- br_port_cfg.vlan_dst_mac_vid_enable = true; ++ priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE; ++ priv->ports[port].learning = true; + + /* include all assigned user ports in the CPU portmap */ ++ bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS); + dsa_switch_for_each_user_port(dp, ds) { + /* it's safe to rely on cpu_dp being valid for user ports */ + if (dp->cpu_dp->index != port) + continue; + +- bridge_port_map |= BIT(dp->index); ++ __set_bit(dp->index, priv->ports[port].portmap); + } +- br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map); + +- return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg); ++ return mxl862xx_set_bridge_port(ds, port); + } + +-static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port) ++static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port, ++ const struct dsa_bridge bridge, ++ bool *tx_fwd_offload, ++ struct netlink_ext_ack *extack) + { +- struct mxl862xx_bridge_port_config br_port_cfg = {}; +- struct dsa_port *dp = dsa_to_port(ds, port); +- struct mxl862xx_bridge_alloc br_alloc = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ u16 fw_id; + int ret; + +- ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc); +- if (ret) { +- dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port); +- return ret; ++ if (!priv->bridges[bridge.num]) { ++ ret = mxl862xx_allocate_bridge(priv, &fw_id); ++ if (ret) ++ return ret; ++ ++ priv->bridges[bridge.num] = fw_id; ++ ++ /* Free bridge here on error, DSA rollback won't. */ ++ ret = mxl862xx_sync_bridge_members(ds, &bridge); ++ if (ret) { ++ mxl862xx_free_bridge(ds, &bridge); ++ return ret; ++ } ++ ++ return 0; + } + +- br_port_cfg.bridge_id = br_alloc.bridge_id; +- br_port_cfg.bridge_port_id = cpu_to_le16(port); +- br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | +- MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | +- MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | +- MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); +- br_port_cfg.src_mac_learning_disable = true; +- br_port_cfg.vlan_src_mac_vid_enable = false; +- br_port_cfg.vlan_dst_mac_vid_enable = false; +- /* As this function is only called for user ports it is safe to rely on +- * cpu_dp being valid ++ return mxl862xx_sync_bridge_members(ds, &bridge); ++} ++ ++static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port, ++ const struct dsa_bridge bridge) ++{ ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ int err; ++ ++ err = mxl862xx_sync_bridge_members(ds, &bridge); ++ if (err) ++ dev_err(ds->dev, ++ "failed to sync bridge members after port %d left: %pe\n", ++ port, ERR_PTR(err)); ++ ++ /* Revert leaving port, omitted by the sync above, to its ++ * single-port bridge + */ +- br_port_cfg.bridge_port_map[0] = cpu_to_le16(BIT(dp->cpu_dp->index)); ++ bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS); ++ __set_bit(dp->cpu_dp->index, p->portmap); ++ p->flood_block = 0; ++ err = mxl862xx_set_bridge_port(ds, port); ++ if (err) ++ dev_err(ds->dev, ++ "failed to update bridge port %d state: %pe\n", port, ++ ERR_PTR(err)); + +- return MXL862XX_API_WRITE(ds->priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg); ++ if (!dsa_bridge_ports(ds, bridge.dev)) ++ mxl862xx_free_bridge(ds, &bridge); + } + + static int mxl862xx_port_setup(struct dsa_switch *ds, int port) + { ++ struct mxl862xx_priv *priv = ds->priv; + struct dsa_port *dp = dsa_to_port(ds, port); + bool is_cpu_port = dsa_port_is_cpu(dp); + int ret; +@@ -352,7 +610,31 @@ static int mxl862xx_port_setup(struct ds + return mxl862xx_setup_cpu_bridge(ds, port); + + /* setup single-port bridge for user ports */ +- return mxl862xx_add_single_port_bridge(ds, port); ++ ret = mxl862xx_add_single_port_bridge(ds, port); ++ if (ret) ++ return ret; ++ ++ priv->ports[port].setup_done = true; ++ ++ return 0; ++} ++ ++static void mxl862xx_port_teardown(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ ++ if (dsa_port_is_unused(dp) || dsa_port_is_dsa(dp)) ++ return; ++ ++ /* Prevent deferred host_flood_work from acting on stale state. ++ * The flag is checked under rtnl_lock() by the worker; since ++ * teardown also runs under RTNL, this is race-free. ++ * ++ * HW EVLAN/VF blocks are not freed here -- the firmware receives ++ * a full reset on the next probe, which reclaims all resources. ++ */ ++ priv->ports[port].setup_done = false; + } + + static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, +@@ -365,14 +647,385 @@ static void mxl862xx_phylink_get_caps(st + config->supported_interfaces); + } + ++static int mxl862xx_get_fid(struct dsa_switch *ds, struct dsa_db db) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ ++ switch (db.type) { ++ case DSA_DB_PORT: ++ return priv->ports[db.dp->index].fid; ++ ++ case DSA_DB_BRIDGE: ++ if (!priv->bridges[db.bridge.num]) ++ return -ENOENT; ++ return priv->bridges[db.bridge.num]; ++ ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int mxl862xx_port_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid, struct dsa_db db) ++{ ++ struct mxl862xx_mac_table_add param = {}; ++ int fid = mxl862xx_get_fid(ds, db), ret; ++ struct mxl862xx_priv *priv = ds->priv; ++ ++ if (fid < 0) ++ return fid; ++ ++ param.port_id = cpu_to_le32(port); ++ param.static_entry = true; ++ param.fid = cpu_to_le16(fid); ++ param.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); ++ ether_addr_copy(param.mac, addr); ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, param); ++ if (ret) ++ dev_err(ds->dev, "failed to add FDB entry on port %d\n", port); ++ ++ return ret; ++} ++ ++static int mxl862xx_port_fdb_del(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid, const struct dsa_db db) ++{ ++ struct mxl862xx_mac_table_remove param = {}; ++ int fid = mxl862xx_get_fid(ds, db), ret; ++ struct mxl862xx_priv *priv = ds->priv; ++ ++ if (fid < 0) ++ return fid; ++ ++ param.fid = cpu_to_le16(fid); ++ param.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); ++ ether_addr_copy(param.mac, addr); ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, param); ++ if (ret) ++ dev_err(ds->dev, "failed to remove FDB entry on port %d\n", port); ++ ++ return ret; ++} ++ ++static int mxl862xx_port_fdb_dump(struct dsa_switch *ds, int port, ++ dsa_fdb_dump_cb_t *cb, void *data) ++{ ++ struct mxl862xx_mac_table_read param = { .initial = 1 }; ++ struct mxl862xx_priv *priv = ds->priv; ++ u32 entry_port_id; ++ int ret; ++ ++ while (true) { ++ ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYREAD, param); ++ if (ret) ++ return ret; ++ ++ if (param.last) ++ break; ++ ++ entry_port_id = le32_to_cpu(param.port_id); ++ ++ if (entry_port_id == port) { ++ ret = cb(param.mac, FIELD_GET(MXL862XX_TCI_VLAN_ID, ++ le16_to_cpu(param.tci)), ++ param.static_entry, data); ++ if (ret) ++ return ret; ++ } ++ ++ memset(¶m, 0, sizeof(param)); ++ } ++ ++ return 0; ++} ++ ++static int mxl862xx_port_mdb_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb, ++ const struct dsa_db db) ++{ ++ struct mxl862xx_mac_table_query qparam = {}; ++ struct mxl862xx_mac_table_add aparam = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ int fid, ret; ++ ++ fid = mxl862xx_get_fid(ds, db); ++ if (fid < 0) ++ return fid; ++ ++ /* Look up existing entry by {MAC, FID, TCI} */ ++ ether_addr_copy(qparam.mac, mdb->addr); ++ qparam.fid = cpu_to_le16(fid); ++ qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYQUERY, qparam); ++ if (ret) ++ return ret; ++ ++ /* Build the ADD command using portmap mode */ ++ ether_addr_copy(aparam.mac, mdb->addr); ++ aparam.fid = cpu_to_le16(fid); ++ aparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); ++ aparam.static_entry = true; ++ aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG); ++ ++ /* Merge with existing portmap if entry already exists */ ++ if (qparam.found) ++ memcpy(aparam.port_map, qparam.port_map, ++ sizeof(aparam.port_map)); ++ ++ mxl862xx_fw_portmap_set_bit(aparam.port_map, port); ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, aparam); ++} ++ ++static int mxl862xx_port_mdb_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb, ++ const struct dsa_db db) ++{ ++ struct mxl862xx_mac_table_remove rparam = {}; ++ struct mxl862xx_mac_table_query qparam = {}; ++ struct mxl862xx_mac_table_add aparam = {}; ++ int fid = mxl862xx_get_fid(ds, db), ret; ++ struct mxl862xx_priv *priv = ds->priv; ++ ++ if (fid < 0) ++ return fid; ++ ++ /* Look up existing entry */ ++ qparam.fid = cpu_to_le16(fid); ++ qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); ++ ether_addr_copy(qparam.mac, mdb->addr); ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYQUERY, qparam); ++ if (ret) ++ return ret; ++ ++ if (!qparam.found) ++ return 0; ++ ++ mxl862xx_fw_portmap_clear_bit(qparam.port_map, port); ++ ++ if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) { ++ /* No ports left — remove the entry entirely */ ++ rparam.fid = cpu_to_le16(fid); ++ rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); ++ ether_addr_copy(rparam.mac, mdb->addr); ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, rparam); ++ } else { ++ /* Write back with reduced portmap */ ++ aparam.fid = cpu_to_le16(fid); ++ aparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); ++ ether_addr_copy(aparam.mac, mdb->addr); ++ aparam.static_entry = true; ++ aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG); ++ memcpy(aparam.port_map, qparam.port_map, sizeof(aparam.port_map)); ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, aparam); ++ } ++ ++ return ret; ++} ++ ++static int mxl862xx_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) ++{ ++ struct mxl862xx_cfg param = {}; ++ int ret; ++ ++ ret = MXL862XX_API_READ(ds->priv, MXL862XX_COMMON_CFGGET, param); ++ if (ret) { ++ dev_err(ds->dev, "failed to read switch config\n"); ++ return ret; ++ } ++ ++ param.mac_table_age_timer = cpu_to_le32(MXL862XX_AGETIMER_CUSTOM); ++ param.age_timer = cpu_to_le32(msecs / 1000); ++ ret = MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_CFGSET, param); ++ if (ret) ++ dev_err(ds->dev, "failed to set ageing\n"); ++ ++ return ret; ++} ++ ++static void mxl862xx_port_stp_state_set(struct dsa_switch *ds, int port, ++ u8 state) ++{ ++ struct mxl862xx_stp_port_cfg param = { ++ .port_id = cpu_to_le16(port), ++ }; ++ struct mxl862xx_priv *priv = ds->priv; ++ int ret; ++ ++ switch (state) { ++ case BR_STATE_DISABLED: ++ param.port_state = cpu_to_le32(MXL862XX_STP_PORT_STATE_DISABLE); ++ break; ++ case BR_STATE_BLOCKING: ++ case BR_STATE_LISTENING: ++ param.port_state = cpu_to_le32(MXL862XX_STP_PORT_STATE_BLOCKING); ++ break; ++ case BR_STATE_LEARNING: ++ param.port_state = cpu_to_le32(MXL862XX_STP_PORT_STATE_LEARNING); ++ break; ++ case BR_STATE_FORWARDING: ++ param.port_state = cpu_to_le32(MXL862XX_STP_PORT_STATE_FORWARD); ++ break; ++ default: ++ dev_err(ds->dev, "invalid STP state: %d\n", state); ++ return; ++ } ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_STP_PORTCFGSET, param); ++ if (ret) { ++ dev_err(ds->dev, "failed to set STP state on port %d\n", port); ++ return; ++ } ++ ++ /* The firmware may re-enable MAC learning as a side-effect of entering ++ * LEARNING or FORWARDING state (per 802.1D defaults). ++ * Re-apply the driver's intended learning and metering config so that ++ * standalone ports keep learning disabled. ++ * This is likely to get fixed in future firmware releases, however, ++ * the additional API call even then doesn't hurt much. ++ */ ++ ret = mxl862xx_set_bridge_port(ds, port); ++ if (ret) ++ dev_err(ds->dev, "failed to reapply brport flags on port %d\n", ++ port); ++ ++ mxl862xx_port_fast_age(ds, port); ++} ++ ++/* Deferred work handler for host flood configuration. ++ * ++ * port_set_host_flood is called from atomic context (under ++ * netif_addr_lock), so firmware calls must be deferred. The worker ++ * acquires rtnl_lock() to serialize with DSA callbacks that access the ++ * same driver state. ++ */ ++static void mxl862xx_host_flood_work_fn(struct work_struct *work) ++{ ++ struct mxl862xx_port *p = container_of(work, struct mxl862xx_port, ++ host_flood_work); ++ struct mxl862xx_priv *priv = p->priv; ++ struct dsa_switch *ds = priv->ds; ++ int port = p - priv->ports; ++ bool uc, mc; ++ ++ rtnl_lock(); ++ ++ /* Port may have been torn down between scheduling and now. */ ++ if (!p->setup_done) { ++ rtnl_unlock(); ++ return; ++ } ++ ++ uc = p->host_flood_uc; ++ mc = p->host_flood_mc; ++ ++ /* The hardware controls unknown-unicast/multicast forwarding per FID ++ * (bridge), not per source port. For bridged ports all members share ++ * one FID, so we cannot selectively suppress flooding to the CPU for ++ * one source port while allowing it for another. Silently ignore the ++ * request -- the excess flooding towards the CPU is harmless. ++ */ ++ if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port))) ++ mxl862xx_bridge_config_fwd(ds, p->fid, uc, mc, true); ++ ++ rtnl_unlock(); ++} ++ ++static void mxl862xx_port_set_host_flood(struct dsa_switch *ds, int port, ++ bool uc, bool mc) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ ++ p->host_flood_uc = uc; ++ p->host_flood_mc = mc; ++ schedule_work(&p->host_flood_work); ++} ++ ++static int mxl862xx_port_pre_bridge_flags(struct dsa_switch *ds, int port, ++ const struct switchdev_brport_flags flags, ++ struct netlink_ext_ack *extack) ++{ ++ if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | ++ BR_LEARNING)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int mxl862xx_port_bridge_flags(struct dsa_switch *ds, int port, ++ const struct switchdev_brport_flags flags, ++ struct netlink_ext_ack *extack) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ unsigned long old_block = priv->ports[port].flood_block; ++ unsigned long block = old_block; ++ bool need_update = false; ++ int ret; ++ ++ if (flags.mask & BR_FLOOD) { ++ if (flags.val & BR_FLOOD) ++ block &= ~BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC); ++ else ++ block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC); ++ } ++ ++ if (flags.mask & BR_MCAST_FLOOD) { ++ if (flags.val & BR_MCAST_FLOOD) { ++ block &= ~BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP); ++ block &= ~BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP); ++ } else { ++ block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP); ++ block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP); ++ } ++ } ++ ++ if (flags.mask & BR_BCAST_FLOOD) { ++ if (flags.val & BR_BCAST_FLOOD) ++ block &= ~BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST); ++ else ++ block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST); ++ } ++ ++ if (flags.mask & BR_LEARNING) ++ priv->ports[port].learning = !!(flags.val & BR_LEARNING); ++ ++ need_update = (block != old_block) || (flags.mask & BR_LEARNING); ++ if (need_update) { ++ priv->ports[port].flood_block = block; ++ ret = mxl862xx_set_bridge_port(ds, port); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ + static const struct dsa_switch_ops mxl862xx_switch_ops = { + .get_tag_protocol = mxl862xx_get_tag_protocol, + .setup = mxl862xx_setup, + .port_setup = mxl862xx_port_setup, ++ .port_teardown = mxl862xx_port_teardown, + .phylink_get_caps = mxl862xx_phylink_get_caps, + .port_enable = mxl862xx_port_enable, + .port_disable = mxl862xx_port_disable, + .port_fast_age = mxl862xx_port_fast_age, ++ .set_ageing_time = mxl862xx_set_ageing_time, ++ .port_bridge_join = mxl862xx_port_bridge_join, ++ .port_bridge_leave = mxl862xx_port_bridge_leave, ++ .port_pre_bridge_flags = mxl862xx_port_pre_bridge_flags, ++ .port_bridge_flags = mxl862xx_port_bridge_flags, ++ .port_stp_state_set = mxl862xx_port_stp_state_set, ++ .port_set_host_flood = mxl862xx_port_set_host_flood, ++ .port_fdb_add = mxl862xx_port_fdb_add, ++ .port_fdb_del = mxl862xx_port_fdb_del, ++ .port_fdb_dump = mxl862xx_port_fdb_dump, ++ .port_mdb_add = mxl862xx_port_mdb_add, ++ .port_mdb_del = mxl862xx_port_mdb_del, + }; + + static void mxl862xx_phylink_mac_config(struct phylink_config *config, +@@ -407,6 +1060,7 @@ static int mxl862xx_probe(struct mdio_de + struct device *dev = &mdiodev->dev; + struct mxl862xx_priv *priv; + struct dsa_switch *ds; ++ int i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -424,8 +1078,17 @@ static int mxl862xx_probe(struct mdio_de + ds->ops = &mxl862xx_switch_ops; + ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops; + ds->num_ports = MXL862XX_MAX_PORTS; ++ ds->fdb_isolation = true; ++ ds->max_num_bridges = MXL862XX_MAX_BRIDGES; ++ + mxl862xx_host_init(priv); + ++ for (i = 0; i < MXL862XX_MAX_PORTS; i++) { ++ priv->ports[i].priv = priv; ++ INIT_WORK(&priv->ports[i].host_flood_work, ++ mxl862xx_host_flood_work_fn); ++ } ++ + dev_set_drvdata(dev, ds); + + return dsa_register_switch(ds); +@@ -435,6 +1098,7 @@ static void mxl862xx_remove(struct mdio_ + { + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); + struct mxl862xx_priv *priv; ++ int i; + + if (!ds) + return; +@@ -444,12 +1108,21 @@ static void mxl862xx_remove(struct mdio_ + dsa_unregister_switch(ds); + + mxl862xx_host_shutdown(priv); ++ ++ /* Cancel any pending host flood work. dsa_unregister_switch() ++ * has already called port_teardown (which sets setup_done=false), ++ * but a worker could still be blocked on rtnl_lock(). Since we ++ * are now outside RTNL, cancel_work_sync() will not deadlock. ++ */ ++ for (i = 0; i < MXL862XX_MAX_PORTS; i++) ++ cancel_work_sync(&priv->ports[i].host_flood_work); + } + + static void mxl862xx_shutdown(struct mdio_device *mdiodev) + { + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); + struct mxl862xx_priv *priv; ++ int i; + + if (!ds) + return; +@@ -460,6 +1133,9 @@ static void mxl862xx_shutdown(struct mdi + + mxl862xx_host_shutdown(priv); + ++ for (i = 0; i < MXL862XX_MAX_PORTS; i++) ++ cancel_work_sync(&priv->ports[i].host_flood_work); ++ + dev_set_drvdata(&mdiodev->dev, NULL); + } + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -4,15 +4,148 @@ + #define __MXL862XX_H + + #include ++#include + #include + ++struct mxl862xx_priv; ++ + #define MXL862XX_MAX_PORTS 17 ++#define MXL862XX_DEFAULT_BRIDGE 0 ++#define MXL862XX_MAX_BRIDGES 48 ++#define MXL862XX_MAX_BRIDGE_PORTS 128 ++ ++/* Number of __le16 words in a firmware portmap (128-bit bitmap). */ ++#define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16) ++ ++/** ++ * mxl862xx_fw_portmap_from_bitmap - convert a kernel bitmap to a firmware ++ * portmap (__le16[8]) ++ * @dst: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries) ++ * @src: kernel bitmap of at least MXL862XX_MAX_BRIDGE_PORTS bits ++ */ ++static inline void ++mxl862xx_fw_portmap_from_bitmap(__le16 *dst, const unsigned long *src) ++{ ++ int i; ++ ++ for (i = 0; i < MXL862XX_FW_PORTMAP_WORDS; i++) ++ dst[i] = cpu_to_le16(bitmap_read(src, i * 16, 16)); ++} ++ ++/** ++ * mxl862xx_fw_portmap_to_bitmap - convert a firmware portmap (__le16[8]) to ++ * a kernel bitmap ++ * @dst: kernel bitmap of at least MXL862XX_MAX_BRIDGE_PORTS bits ++ * @src: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries) ++ */ ++static inline void ++mxl862xx_fw_portmap_to_bitmap(unsigned long *dst, const __le16 *src) ++{ ++ int i; ++ ++ bitmap_zero(dst, MXL862XX_MAX_BRIDGE_PORTS); ++ for (i = 0; i < MXL862XX_FW_PORTMAP_WORDS; i++) ++ bitmap_write(dst, le16_to_cpu(src[i]), i * 16, 16); ++} ++ ++/** ++ * mxl862xx_fw_portmap_set_bit - set a single port bit in a firmware portmap ++ * @map: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries) ++ * @port: port index (0..MXL862XX_MAX_BRIDGE_PORTS-1) ++ */ ++static inline void mxl862xx_fw_portmap_set_bit(__le16 *map, int port) ++{ ++ map[port / 16] |= cpu_to_le16(BIT(port % 16)); ++} ++ ++/** ++ * mxl862xx_fw_portmap_clear_bit - clear a single port bit in a firmware portmap ++ * @map: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries) ++ * @port: port index (0..MXL862XX_MAX_BRIDGE_PORTS-1) ++ */ ++static inline void mxl862xx_fw_portmap_clear_bit(__le16 *map, int port) ++{ ++ map[port / 16] &= ~cpu_to_le16(BIT(port % 16)); ++} ++ ++/** ++ * mxl862xx_fw_portmap_is_empty - check whether a firmware portmap has no ++ * bits set ++ * @map: firmware portmap array (MXL862XX_FW_PORTMAP_WORDS entries) ++ * ++ * Return: true if every word in @map is zero. ++ */ ++static inline bool mxl862xx_fw_portmap_is_empty(const __le16 *map) ++{ ++ int i; ++ ++ for (i = 0; i < MXL862XX_FW_PORTMAP_WORDS; i++) ++ if (map[i]) ++ return false; ++ return true; ++} ++ ++/** ++ * struct mxl862xx_port - per-port state tracked by the driver ++ * @priv: back-pointer to switch private data; needed by ++ * deferred work handlers to access ds and priv ++ * @fid: firmware FID for the permanent single-port bridge; ++ * kept alive for the lifetime of the port so traffic is ++ * never forwarded while the port is unbridged ++ * @portmap: bitmap of switch port indices that share the current ++ * bridge with this port ++ * @flood_block: bitmask of firmware meter indices that are currently ++ * rate-limiting flood traffic on this port (zero-rate ++ * meters used to block flooding) ++ * @learning: true when address learning is enabled on this port ++ * @setup_done: set at end of port_setup, cleared at start of ++ * port_teardown; guards deferred work against ++ * acting on torn-down state ++ * @host_flood_uc: desired host unicast flood state (true = flood); ++ * updated atomically by port_set_host_flood, consumed ++ * by the deferred host_flood_work ++ * @host_flood_mc: desired host multicast flood state (true = flood) ++ * @host_flood_work: deferred work for applying host flood changes; ++ * port_set_host_flood runs in atomic context (under ++ * netif_addr_lock) so firmware calls must be deferred. ++ * The worker acquires rtnl_lock() to serialize with ++ * DSA callbacks and checks @setup_done to avoid ++ * acting on torn-down ports. ++ */ ++struct mxl862xx_port { ++ struct mxl862xx_priv *priv; ++ u16 fid; ++ DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS); ++ unsigned long flood_block; ++ bool learning; ++ bool setup_done; ++ bool host_flood_uc; ++ bool host_flood_mc; ++ struct work_struct host_flood_work; ++}; + ++/** ++ * struct mxl862xx_priv - driver private data for an MxL862xx switch ++ * @ds: pointer to the DSA switch instance ++ * @mdiodev: MDIO device used to communicate with the switch firmware ++ * @crc_err_work: deferred work for shutting down all ports on MDIO CRC errors ++ * @crc_err: set atomically before CRC-triggered shutdown, cleared after ++ * @drop_meter: index of the single shared zero-rate firmware meter used ++ * to unconditionally drop traffic (used to block flooding) ++ * @ports: per-port state, indexed by switch port number ++ * @bridges: maps DSA bridge number to firmware bridge ID; ++ * zero means no firmware bridge allocated for that ++ * DSA bridge number. Indexed by dsa_bridge.num ++ * (0 .. ds->max_num_bridges). ++ */ + struct mxl862xx_priv { + struct dsa_switch *ds; + struct mdio_device *mdiodev; + struct work_struct crc_err_work; + unsigned long crc_err; ++ u16 drop_meter; ++ struct mxl862xx_port ports[MXL862XX_MAX_PORTS]; ++ u16 bridges[MXL862XX_MAX_BRIDGES + 1]; + }; + + #endif /* __MXL862XX_H */ diff --git a/target/linux/generic/pending-6.12/760-05-net-dsa-mxl862xx-implement-VLAN-functionality.patch b/target/linux/generic/pending-6.12/760-05-net-dsa-mxl862xx-implement-VLAN-functionality.patch new file mode 100644 index 00000000000..109808792cb --- /dev/null +++ b/target/linux/generic/pending-6.12/760-05-net-dsa-mxl862xx-implement-VLAN-functionality.patch @@ -0,0 +1,1652 @@ +From 7286ac4f850339aac37dd52633f4a70816b621a8 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 10 Mar 2026 02:36:00 +0000 +Subject: [PATCH 14/35] net: dsa: mxl862xx: implement VLAN functionality + +Add VLAN support using both the Extended VLAN (EVLAN) engine and the +VLAN Filter (VF) engine in a hybrid architecture that allows a higher +number of VIDs than either engine could achieve alone. + +The VLAN Filter engine handles per-port VID membership checks with +discard-unmatched semantics. The Extended VLAN engine handles PVID +insertion on ingress (via fixed catchall rules) and tag stripping on +egress (2 rules per untagged VID). Tagged-only VIDs need no EVLAN +egress rules at all, so they consume only a VF entry. + +Both engines draw from shared 1024-entry hardware pools. The VF pool +is divided equally among user ports for VID membership, while the +EVLAN pool is partitioned into small fixed-size ingress blocks (7 +entries of catchall rules per port) and variable-size egress blocks +for tag stripping. + +With 5 user ports this yields up to 204 VIDs per port (limited by VF), +of which up to 98 can be untagged (limited by EVLAN egress budget). +With 9 user ports the numbers are 113 total and 53 untagged. + +Wire up .port_vlan_add, .port_vlan_del, and .port_vlan_filtering. +Reprogram all EVLAN rules when the PVID or filtering mode changes. +Detach blocks from the bridge port before freeing them on bridge leave +to satisfy the firmware's internal refcount. + +Future optimizations could increase VID capacity by dynamically sizing +the egress EVLAN blocks based on actual per-port untagged VID counts +rather than worst-case pre-allocation, or by sharing EVLAN egress and +VLAN Filter blocks across ports with identical VID sets. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 ++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 12 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 960 +++++++++++++++++++++++- + drivers/net/dsa/mxl862xx/mxl862xx.h | 110 ++- + 4 files changed, 1386 insertions(+), 25 deletions(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -732,6 +732,335 @@ struct mxl862xx_cfg { + } __packed; + + /** ++ * enum mxl862xx_extended_vlan_filter_type - Extended VLAN filter tag type ++ * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL: Normal tagged ++ * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER: No filter (wildcard) ++ * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT: Default entry ++ * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG: Untagged ++ */ ++enum mxl862xx_extended_vlan_filter_type { ++ MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL = 0, ++ MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER = 1, ++ MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT = 2, ++ MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG = 3, ++}; ++ ++/** ++ * enum mxl862xx_extended_vlan_filter_tpid - Extended VLAN filter TPID ++ * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER: No TPID filter ++ * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q: 802.1Q TPID ++ * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE: VLAN type extension ++ */ ++enum mxl862xx_extended_vlan_filter_tpid { ++ MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER = 0, ++ MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q = 1, ++ MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE = 2, ++}; ++ ++/** ++ * enum mxl862xx_extended_vlan_filter_dei - Extended VLAN filter DEI ++ * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER: No DEI filter ++ * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_0: DEI = 0 ++ * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_1: DEI = 1 ++ */ ++enum mxl862xx_extended_vlan_filter_dei { ++ MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER = 0, ++ MXL862XX_EXTENDEDVLAN_FILTER_DEI_0 = 1, ++ MXL862XX_EXTENDEDVLAN_FILTER_DEI_1 = 2, ++}; ++ ++/** ++ * enum mxl862xx_extended_vlan_treatment_remove_tag - Tag removal action ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG: Do not remove tag ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG: Remove one tag ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG: Remove two tags ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM: Discard frame ++ */ ++enum mxl862xx_extended_vlan_treatment_remove_tag { ++ MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG = 0, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG = 1, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG = 2, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM = 3, ++}; ++ ++/** ++ * enum mxl862xx_extended_vlan_treatment_priority - Treatment priority mode ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL: Use explicit value ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY: Copy from inner tag ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY: Copy from outer tag ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP: Derive from DSCP ++ */ ++enum mxl862xx_extended_vlan_treatment_priority { ++ MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY = 1, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY = 2, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3, ++}; ++ ++/** ++ * enum mxl862xx_extended_vlan_treatment_vid - Treatment VID mode ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL: Use explicit VID value ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID: Copy from inner tag ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID: Copy from outer tag ++ */ ++enum mxl862xx_extended_vlan_treatment_vid { ++ MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL = 0, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID = 1, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID = 2, ++}; ++ ++/** ++ * enum mxl862xx_extended_vlan_treatment_tpid - Treatment TPID mode ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID: Copy from inner tag ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID: Copy from outer tag ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE: Use VLAN type extension ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q: Use 802.1Q TPID ++ */ ++enum mxl862xx_extended_vlan_treatment_tpid { ++ MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID = 0, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID = 1, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE = 2, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q = 3, ++}; ++ ++/** ++ * enum mxl862xx_extended_vlan_treatment_dei - Treatment DEI mode ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI: Copy from inner tag ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI: Copy from outer tag ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0: Set DEI to 0 ++ * @MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1: Set DEI to 1 ++ */ ++enum mxl862xx_extended_vlan_treatment_dei { ++ MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI = 0, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI = 1, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0 = 2, ++ MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1 = 3, ++}; ++ ++/** ++ * enum mxl862xx_extended_vlan_4_tpid_mode - 4-TPID mode selector ++ * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1: VLAN TPID type 1 ++ * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2: VLAN TPID type 2 ++ * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3: VLAN TPID type 3 ++ * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4: VLAN TPID type 4 ++ */ ++enum mxl862xx_extended_vlan_4_tpid_mode { ++ MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1 = 0, ++ MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2 = 1, ++ MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3 = 2, ++ MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4 = 3, ++}; ++ ++/** ++ * enum mxl862xx_extended_vlan_filter_ethertype - Filter EtherType match ++ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER: No filter ++ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE: IPoE ++ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE: PPPoE ++ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP: ARP ++ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE: IPv6 IPoE ++ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL: EAPOL ++ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4: DHCPv4 ++ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6: DHCPv6 ++ */ ++enum mxl862xx_extended_vlan_filter_ethertype { ++ MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER = 0, ++ MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE = 1, ++ MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE = 2, ++ MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP = 3, ++ MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE = 4, ++ MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL = 5, ++ MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4 = 6, ++ MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6 = 7, ++}; ++ ++/** ++ * struct mxl862xx_extendedvlan_filter_vlan - Per-tag filter in Extended VLAN ++ * @type: Tag presence/type match (see &enum mxl862xx_extended_vlan_filter_type) ++ * @priority_enable: Enable PCP value matching ++ * @priority_val: PCP value to match ++ * @vid_enable: Enable VID matching ++ * @vid_val: VID value to match ++ * @tpid: TPID match mode (see &enum mxl862xx_extended_vlan_filter_tpid) ++ * @dei: DEI match mode (see &enum mxl862xx_extended_vlan_filter_dei) ++ */ ++struct mxl862xx_extendedvlan_filter_vlan { ++ __le32 type; ++ u8 priority_enable; ++ __le32 priority_val; ++ u8 vid_enable; ++ __le32 vid_val; ++ __le32 tpid; ++ __le32 dei; ++} __packed; ++ ++/** ++ * struct mxl862xx_extendedvlan_filter - Extended VLAN filter configuration ++ * @original_packet_filter_mode: If true, filter on original (pre-treatment) ++ * packet ++ * @filter_4_tpid_mode: 4-TPID mode (see &enum mxl862xx_extended_vlan_4_tpid_mode) ++ * @outer_vlan: Outer VLAN tag filter ++ * @inner_vlan: Inner VLAN tag filter ++ * @ether_type: EtherType filter (see ++ * &enum mxl862xx_extended_vlan_filter_ethertype) ++ */ ++struct mxl862xx_extendedvlan_filter { ++ u8 original_packet_filter_mode; ++ __le32 filter_4_tpid_mode; ++ struct mxl862xx_extendedvlan_filter_vlan outer_vlan; ++ struct mxl862xx_extendedvlan_filter_vlan inner_vlan; ++ __le32 ether_type; ++} __packed; ++ ++/** ++ * struct mxl862xx_extendedvlan_treatment_vlan - Per-tag treatment in ++ * Extended VLAN ++ * @priority_mode: Priority assignment mode ++ * (see &enum mxl862xx_extended_vlan_treatment_priority) ++ * @priority_val: Priority value (when mode is VAL) ++ * @vid_mode: VID assignment mode ++ * (see &enum mxl862xx_extended_vlan_treatment_vid) ++ * @vid_val: VID value (when mode is VAL) ++ * @tpid: TPID assignment mode ++ * (see &enum mxl862xx_extended_vlan_treatment_tpid) ++ * @dei: DEI assignment mode ++ * (see &enum mxl862xx_extended_vlan_treatment_dei) ++ */ ++struct mxl862xx_extendedvlan_treatment_vlan { ++ __le32 priority_mode; ++ __le32 priority_val; ++ __le32 vid_mode; ++ __le32 vid_val; ++ __le32 tpid; ++ __le32 dei; ++} __packed; ++ ++/** ++ * struct mxl862xx_extendedvlan_treatment - Extended VLAN treatment ++ * @remove_tag: Tag removal action ++ * (see &enum mxl862xx_extended_vlan_treatment_remove_tag) ++ * @treatment_4_tpid_mode: 4-TPID treatment mode ++ * @add_outer_vlan: Add outer VLAN tag ++ * @outer_vlan: Outer VLAN tag treatment parameters ++ * @add_inner_vlan: Add inner VLAN tag ++ * @inner_vlan: Inner VLAN tag treatment parameters ++ * @reassign_bridge_port: Reassign to different bridge port ++ * @new_bridge_port_id: New bridge port ID ++ * @new_dscp_enable: Enable new DSCP assignment ++ * @new_dscp: New DSCP value ++ * @new_traffic_class_enable: Enable new traffic class assignment ++ * @new_traffic_class: New traffic class value ++ * @new_meter_enable: Enable new metering ++ * @s_new_traffic_meter_id: New traffic meter ID ++ * @dscp2pcp_map: DSCP to PCP mapping table (64 entries) ++ * @loopback_enable: Enable loopback ++ * @da_sa_swap_enable: Enable DA/SA swap ++ * @mirror_enable: Enable mirroring ++ */ ++struct mxl862xx_extendedvlan_treatment { ++ __le32 remove_tag; ++ __le32 treatment_4_tpid_mode; ++ u8 add_outer_vlan; ++ struct mxl862xx_extendedvlan_treatment_vlan outer_vlan; ++ u8 add_inner_vlan; ++ struct mxl862xx_extendedvlan_treatment_vlan inner_vlan; ++ u8 reassign_bridge_port; ++ __le16 new_bridge_port_id; ++ u8 new_dscp_enable; ++ __le16 new_dscp; ++ u8 new_traffic_class_enable; ++ u8 new_traffic_class; ++ u8 new_meter_enable; ++ __le16 s_new_traffic_meter_id; ++ u8 dscp2pcp_map[64]; ++ u8 loopback_enable; ++ u8 da_sa_swap_enable; ++ u8 mirror_enable; ++} __packed; ++ ++/** ++ * struct mxl862xx_extendedvlan_alloc - Extended VLAN block allocation ++ * @number_of_entries: Number of entries to allocate (input) / allocated ++ * (output) ++ * @extended_vlan_block_id: Block ID assigned by firmware (output on alloc, ++ * input on free) ++ * ++ * Used with %MXL862XX_EXTENDEDVLAN_ALLOC and %MXL862XX_EXTENDEDVLAN_FREE. ++ */ ++struct mxl862xx_extendedvlan_alloc { ++ __le16 number_of_entries; ++ __le16 extended_vlan_block_id; ++} __packed; ++ ++/** ++ * struct mxl862xx_extendedvlan_config - Extended VLAN entry configuration ++ * @extended_vlan_block_id: Block ID from allocation ++ * @entry_index: Entry index within the block ++ * @filter: Filter (match) configuration ++ * @treatment: Treatment (action) configuration ++ * ++ * Used with %MXL862XX_EXTENDEDVLAN_SET and %MXL862XX_EXTENDEDVLAN_GET. ++ */ ++struct mxl862xx_extendedvlan_config { ++ __le16 extended_vlan_block_id; ++ __le16 entry_index; ++ struct mxl862xx_extendedvlan_filter filter; ++ struct mxl862xx_extendedvlan_treatment treatment; ++} __packed; ++ ++/** ++ * enum mxl862xx_vlan_filter_tci_mask - VLAN Filter TCI mask ++ * @MXL862XX_VLAN_FILTER_TCI_MASK_VID: TCI mask for VLAN ID ++ * @MXL862XX_VLAN_FILTER_TCI_MASK_PCP: TCI mask for VLAN PCP ++ * @MXL862XX_VLAN_FILTER_TCI_MASK_TCI: TCI mask for VLAN TCI ++ */ ++enum mxl862xx_vlan_filter_tci_mask { ++ MXL862XX_VLAN_FILTER_TCI_MASK_VID = 0, ++ MXL862XX_VLAN_FILTER_TCI_MASK_PCP = 1, ++ MXL862XX_VLAN_FILTER_TCI_MASK_TCI = 2, ++}; ++ ++/** ++ * struct mxl862xx_vlanfilter_alloc - VLAN Filter block allocation ++ * @number_of_entries: Number of entries to allocate (input) / allocated ++ * (output) ++ * @vlan_filter_block_id: Block ID assigned by firmware (output on alloc, ++ * input on free) ++ * @discard_untagged: Discard untagged packets ++ * @discard_unmatched_tagged: Discard tagged packets that do not match any ++ * entry in the block ++ * @use_default_port_vid: Use default port VLAN ID for filtering ++ * ++ * Used with %MXL862XX_VLANFILTER_ALLOC and %MXL862XX_VLANFILTER_FREE. ++ */ ++struct mxl862xx_vlanfilter_alloc { ++ __le16 number_of_entries; ++ __le16 vlan_filter_block_id; ++ u8 discard_untagged; ++ u8 discard_unmatched_tagged; ++ u8 use_default_port_vid; ++} __packed; ++ ++/** ++ * struct mxl862xx_vlanfilter_config - VLAN Filter entry configuration ++ * @vlan_filter_block_id: Block ID from allocation ++ * @entry_index: Entry index within the block ++ * @vlan_filter_mask: TCI field(s) to match (see ++ * &enum mxl862xx_vlan_filter_tci_mask) ++ * @val: TCI value(s) to match (VID, PCP, or full TCI depending on mask) ++ * @discard_matched: When true, discard frames matching this entry; ++ * when false, allow them ++ * ++ * Used with %MXL862XX_VLANFILTER_SET and %MXL862XX_VLANFILTER_GET. ++ */ ++struct mxl862xx_vlanfilter_config { ++ __le16 vlan_filter_block_id; ++ __le16 entry_index; ++ __le32 vlan_filter_mask; /* enum mxl862xx_vlan_filter_tci_mask */ ++ __le32 val; ++ u8 discard_matched; ++} __packed; ++ ++/** + * enum mxl862xx_ss_sp_tag_mask - Special tag valid field indicator bits + * @MXL862XX_SS_SP_TAG_MASK_RX: valid RX special tag mode + * @MXL862XX_SS_SP_TAG_MASK_TX: valid TX special tag mode +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -17,6 +17,8 @@ + #define MXL862XX_CTP_MAGIC 0x500 + #define MXL862XX_QOS_MAGIC 0x600 + #define MXL862XX_SWMAC_MAGIC 0xa00 ++#define MXL862XX_EXTVLAN_MAGIC 0xb00 ++#define MXL862XX_VLANFILTER_MAGIC 0xc00 + #define MXL862XX_STP_MAGIC 0xf00 + #define MXL862XX_SS_MAGIC 0x1600 + #define GPY_GPY2XX_MAGIC 0x1800 +@@ -47,6 +49,16 @@ + #define MXL862XX_MAC_TABLEENTRYREMOVE (MXL862XX_SWMAC_MAGIC + 0x5) + #define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8) + ++#define MXL862XX_EXTENDEDVLAN_ALLOC (MXL862XX_EXTVLAN_MAGIC + 0x1) ++#define MXL862XX_EXTENDEDVLAN_SET (MXL862XX_EXTVLAN_MAGIC + 0x2) ++#define MXL862XX_EXTENDEDVLAN_GET (MXL862XX_EXTVLAN_MAGIC + 0x3) ++#define MXL862XX_EXTENDEDVLAN_FREE (MXL862XX_EXTVLAN_MAGIC + 0x4) ++ ++#define MXL862XX_VLANFILTER_ALLOC (MXL862XX_VLANFILTER_MAGIC + 0x1) ++#define MXL862XX_VLANFILTER_SET (MXL862XX_VLANFILTER_MAGIC + 0x2) ++#define MXL862XX_VLANFILTER_GET (MXL862XX_VLANFILTER_MAGIC + 0x3) ++#define MXL862XX_VLANFILTER_FREE (MXL862XX_VLANFILTER_MAGIC + 0x4) ++ + #define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x2) + + #define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2) +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -50,6 +50,88 @@ static const int mxl862xx_flood_meters[] + MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST, + }; + ++enum mxl862xx_evlan_action { ++ EVLAN_ACCEPT, /* pass-through, no tag removal */ ++ EVLAN_STRIP_IF_UNTAGGED, /* remove 1 tag if entry's untagged flag set */ ++ EVLAN_DISCARD, /* discard upstream */ ++ EVLAN_PVID_OR_DISCARD, /* insert PVID tag or discard if no PVID */ ++ EVLAN_PVID_OR_PASS, /* insert PVID tag or pass-through */ ++ EVLAN_STRIP1_AND_PVID_OR_DISCARD,/* strip 1 tag + insert PVID, or discard */ ++}; ++ ++struct mxl862xx_evlan_rule_desc { ++ u8 outer_type; /* enum mxl862xx_extended_vlan_filter_type */ ++ u8 inner_type; /* enum mxl862xx_extended_vlan_filter_type */ ++ u8 outer_tpid; /* enum mxl862xx_extended_vlan_filter_tpid */ ++ u8 inner_tpid; /* enum mxl862xx_extended_vlan_filter_tpid */ ++ bool match_vid; /* true: match on VID from the vid parameter */ ++ u8 action; /* enum mxl862xx_evlan_action */ ++}; ++ ++/* Shorthand constants for readability */ ++#define FT_NORMAL MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL ++#define FT_NO_FILTER MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER ++#define FT_DEFAULT MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT ++#define FT_NO_TAG MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG ++#define TP_NONE MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER ++#define TP_8021Q MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q ++ ++/* ++ * VLAN-aware ingress: 7 final catchall rules. ++ * ++ * VLAN Filter handles VID membership for tagged frames, so the ++ * Extended VLAN ingress block only needs to handle: ++ * - Priority-tagged (VID=0): strip + insert PVID ++ * - Untagged: insert PVID or discard ++ * - Standard 802.1Q VID>0: pass through (VF handles membership) ++ * - Non-8021Q TPID (0x88A8 etc.): treat as untagged ++ * ++ * Rule ordering is critical: the EVLAN engine scans entries in ++ * ascending index order and stops at the first match. ++ * ++ * The 802.1Q ACCEPT rules (indices 3--4) must appear BEFORE the ++ * NO_FILTER catchalls (indices 5--6). NO_FILTER matches any tag ++ * regardless of TPID, so without the ACCEPT guard, it would also ++ * catch standard 802.1Q VID>0 frames and corrupt them. With the ++ * guard, 802.1Q VID>0 frames match the ACCEPT rules first and ++ * pass through untouched; only non-8021Q TPID frames fall through ++ * to the NO_FILTER catchalls. ++ */ ++static const struct mxl862xx_evlan_rule_desc ingress_aware_final[] = { ++ /* 802.1p / priority-tagged (VID 0): strip + PVID */ ++ { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, true, EVLAN_STRIP1_AND_PVID_OR_DISCARD }, ++ { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, true, EVLAN_STRIP1_AND_PVID_OR_DISCARD }, ++ /* Untagged: PVID insertion or discard */ ++ { FT_NO_TAG, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_PVID_OR_DISCARD }, ++ /* 802.1Q VID>0: accept - VF handles membership. ++ * match_vid=false means any VID; VID=0 is already caught above. ++ */ ++ { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, false, EVLAN_ACCEPT }, ++ { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, false, EVLAN_ACCEPT }, ++ /* Non-8021Q TPID (0x88A8 etc.): treat as untagged - strip + PVID */ ++ { FT_NO_FILTER, FT_NO_FILTER, TP_NONE, TP_NONE, false, EVLAN_STRIP1_AND_PVID_OR_DISCARD }, ++ { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_STRIP1_AND_PVID_OR_DISCARD }, ++}; ++ ++/* ++ * VID-specific accept rules (VLAN-aware, standard tag, 2 per VID). ++ * Outer tag carries the VLAN; inner may or may not be present. ++ */ ++static const struct mxl862xx_evlan_rule_desc vid_accept_standard[] = { ++ { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED }, ++ { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, true, EVLAN_STRIP_IF_UNTAGGED }, ++}; ++ ++/* ++ * VID-specific accept rules for VLAN-unaware egress. ++ * The HW sees the MxL tag as outer, real VLAN tag as inner. ++ * match on inner VID with outer=NO_FILTER. ++ */ ++static const struct mxl862xx_evlan_rule_desc vid_accept_egress_unaware[] = { ++ { FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED }, ++ { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, true, EVLAN_STRIP_IF_UNTAGGED }, ++}; ++ + static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol m) +@@ -275,6 +357,7 @@ static int mxl862xx_set_bridge_port(stru + struct mxl862xx_port *p = &priv->ports[port]; + u16 bridge_id = dp->bridge ? + priv->bridges[dp->bridge->num] : p->fid; ++ u16 vf_scan; + bool enable; + int i, idx; + +@@ -283,9 +366,69 @@ static int mxl862xx_set_bridge_port(stru + br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | +- MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER); ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); + br_port_cfg.src_mac_learning_disable = !p->learning; + ++ /* Extended VLAN block assignments. ++ * Ingress: block_size is sent as-is (all entries are finals). ++ * Egress: n_active narrows the scan window to only the ++ * entries actually written by evlan_program_egress. ++ */ ++ br_port_cfg.ingress_extended_vlan_enable = p->ingress_evlan.in_use; ++ br_port_cfg.ingress_extended_vlan_block_id = ++ cpu_to_le16(p->ingress_evlan.block_id); ++ br_port_cfg.ingress_extended_vlan_block_size = ++ cpu_to_le16(p->ingress_evlan.block_size); ++ br_port_cfg.egress_extended_vlan_enable = p->egress_evlan.in_use; ++ br_port_cfg.egress_extended_vlan_block_id = ++ cpu_to_le16(p->egress_evlan.block_id); ++ br_port_cfg.egress_extended_vlan_block_size = ++ cpu_to_le16(p->egress_evlan.n_active); ++ ++ /* VLAN Filter block assignments (per-port). ++ * The block_size sent to the firmware narrows the HW scan ++ * window to [block_id, block_id + active_count), relying on ++ * discard_unmatched_tagged for frames outside that range. ++ * When active_count=0, send 1 to scan only the DISCARD ++ * sentinel at index 0 (block_size=0 would disable narrowing ++ * and scan the entire allocated block). ++ * ++ * The bridge check ensures VF is disabled when the port ++ * leaves the bridge, without needing to prematurely clear ++ * vlan_filtering (which the DSA framework handles later via ++ * port_vlan_filtering). ++ */ ++ if (p->vf.allocated && p->vlan_filtering && ++ dsa_port_bridge_dev_get(dp)) { ++ vf_scan = max_t(u16, p->vf.active_count, 1); ++ br_port_cfg.ingress_vlan_filter_enable = 1; ++ br_port_cfg.ingress_vlan_filter_block_id = ++ cpu_to_le16(p->vf.block_id); ++ br_port_cfg.ingress_vlan_filter_block_size = ++ cpu_to_le16(vf_scan); ++ ++ br_port_cfg.egress_vlan_filter1enable = 1; ++ br_port_cfg.egress_vlan_filter1block_id = ++ cpu_to_le16(p->vf.block_id); ++ br_port_cfg.egress_vlan_filter1block_size = ++ cpu_to_le16(vf_scan); ++ } else { ++ br_port_cfg.ingress_vlan_filter_enable = 0; ++ br_port_cfg.egress_vlan_filter1enable = 0; ++ } ++ ++ /* IVL when VLAN-aware: include VID in FDB lookup keys so that ++ * learned entries are per-VID. In VLAN-unaware mode, SVL is ++ * used (VID excluded from key). ++ */ ++ br_port_cfg.vlan_src_mac_vid_enable = p->vlan_filtering; ++ br_port_cfg.vlan_dst_mac_vid_enable = p->vlan_filtering; ++ + mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap); + + for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) { +@@ -329,13 +472,131 @@ static int mxl862xx_sync_bridge_members( + return ret; + } + ++static void mxl862xx_evlan_block_init(struct mxl862xx_evlan_block *blk, ++ u16 size) ++{ ++ blk->allocated = false; ++ blk->in_use = false; ++ blk->block_id = 0; ++ blk->block_size = size; ++ blk->n_active = 0; ++} ++ ++static int mxl862xx_evlan_block_alloc(struct mxl862xx_priv *priv, ++ struct mxl862xx_evlan_block *blk) ++{ ++ struct mxl862xx_extendedvlan_alloc param = {}; ++ int ret; ++ ++ param.number_of_entries = cpu_to_le16(blk->block_size); ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_EXTENDEDVLAN_ALLOC, param); ++ if (ret) ++ return ret; ++ ++ blk->block_id = le16_to_cpu(param.extended_vlan_block_id); ++ blk->allocated = true; ++ ++ return 0; ++} ++ ++/** ++ * mxl862xx_vf_init - Initialize per-port VF block software state ++ * @vf: VLAN Filter block to initialize ++ * @size: block size (entries per port) ++ */ ++static void mxl862xx_vf_init(struct mxl862xx_vf_block *vf, u16 size) ++{ ++ vf->allocated = false; ++ vf->block_id = 0; ++ vf->block_size = size; ++ vf->active_count = 0; ++ INIT_LIST_HEAD(&vf->vids); ++} ++ ++/** ++ * mxl862xx_vf_block_alloc - Allocate a VLAN Filter block from firmware ++ * @priv: driver private data ++ * @size: number of entries to allocate ++ * @block_id: output -- block ID assigned by firmware ++ * ++ * Allocates a contiguous VLAN Filter block and configures it to discard ++ * unmatched tagged frames (VID membership enforcement). ++ */ ++static int mxl862xx_vf_block_alloc(struct mxl862xx_priv *priv, ++ u16 size, u16 *block_id) ++{ ++ struct mxl862xx_vlanfilter_alloc param = {}; ++ int ret; ++ ++ param.number_of_entries = cpu_to_le16(size); ++ param.discard_untagged = 0; ++ param.discard_unmatched_tagged = 1; ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_VLANFILTER_ALLOC, param); ++ if (ret) ++ return ret; ++ ++ *block_id = le16_to_cpu(param.vlan_filter_block_id); ++ return 0; ++} ++ ++/** ++ * mxl862xx_vf_entry_discard - Write a DISCARD entry to plug an unused slot ++ * @priv: driver private data ++ * @block_id: HW VLAN Filter block ID ++ * @index: entry index within the block ++ * ++ * Unwritten VLAN Filter entries default to VID=0 / ALLOW which would ++ * leak VID 0 traffic. This writes a DISCARD entry to plug the slot. ++ */ ++static int mxl862xx_vf_entry_discard(struct mxl862xx_priv *priv, ++ u16 block_id, u16 index) ++{ ++ struct mxl862xx_vlanfilter_config cfg = {}; ++ ++ cfg.vlan_filter_block_id = cpu_to_le16(block_id); ++ cfg.entry_index = cpu_to_le16(index); ++ cfg.vlan_filter_mask = cpu_to_le32(MXL862XX_VLAN_FILTER_TCI_MASK_VID); ++ cfg.val = cpu_to_le32(0); ++ cfg.discard_matched = 1; ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg); ++} ++ ++/** ++ * mxl862xx_vf_alloc - Allocate the port's VF HW block ++ * @priv: driver private data ++ * @vf: VLAN Filter block (must have been initialized via mxl862xx_vf_init) ++ * ++ * Allocates the block and writes a DISCARD sentinel at index 0 so that ++ * when active_count is 0, the single-entry scan window blocks VID-0 ++ * (which would otherwise match the zeroed-out default and be allowed). ++ * Called once per port from port_setup. ++ */ ++static int mxl862xx_vf_alloc(struct mxl862xx_priv *priv, ++ struct mxl862xx_vf_block *vf) ++{ ++ int ret; ++ ++ ret = mxl862xx_vf_block_alloc(priv, vf->block_size, &vf->block_id); ++ if (ret) ++ return ret; ++ ++ vf->allocated = true; ++ vf->active_count = 0; ++ ++ /* Sentinel: block VID-0 when scan window covers only index 0 */ ++ return mxl862xx_vf_entry_discard(priv, vf->block_id, 0); ++} ++ + /** + * mxl862xx_allocate_bridge - Allocate a firmware bridge instance + * @priv: driver private data + * @bridge_id: output -- firmware bridge ID assigned by the firmware + * + * Newly allocated bridges default to flooding all traffic classes +- * (unknown unicast, multicast, broadcast). Callers that need ++ * (unknown unicast, multicast, broadcast). Callers that need + * different forwarding behavior must call mxl862xx_bridge_config_fwd() + * after allocation. + * +@@ -404,6 +665,9 @@ static int mxl862xx_add_single_port_brid + static int mxl862xx_setup(struct dsa_switch *ds) + { + struct mxl862xx_priv *priv = ds->priv; ++ int n_user_ports = 0, max_vlans; ++ int ingress_finals, vid_rules; ++ struct dsa_port *dp; + int ret; + + ret = mxl862xx_reset(priv); +@@ -414,6 +678,50 @@ static int mxl862xx_setup(struct dsa_swi + if (ret) + return ret; + ++ /* Calculate Extended VLAN block sizes. ++ * With VLAN Filter handling VID membership checks: ++ * Ingress: only final catchall rules (PVID insertion, 802.1Q ++ * accept, non-8021Q TPID handling, discard). ++ * Block sized to exactly fit the finals -- no per-VID ++ * ingress EVLAN rules are needed. (7 entries.) ++ * Egress: 2 rules per VID that needs tag stripping (untagged VIDs). ++ * No egress final catchalls -- VLAN Filter does the discard. ++ * CPU: EVLAN is left disabled on CPU ports -- frames pass ++ * through without EVLAN processing. ++ * ++ * Total EVLAN budget: ++ * n_user_ports * (ingress + egress) ≤ 1024. ++ * Ingress blocks are small (7 entries), so almost all capacity ++ * goes to egress VID rules. ++ */ ++ dsa_switch_for_each_user_port(dp, ds) ++ n_user_ports++; ++ ++ if (n_user_ports) { ++ ingress_finals = ARRAY_SIZE(ingress_aware_final); ++ vid_rules = ARRAY_SIZE(vid_accept_standard); ++ ++ /* Ingress block: fixed at finals count (7 entries) */ ++ priv->evlan_ingress_size = ingress_finals; ++ ++ /* Egress block: remaining budget divided equally among ++ * user ports. Each untagged VID needs vid_rules (2) ++ * EVLAN entries for tag stripping. Tagged-only VIDs ++ * need no EVLAN rules at all. ++ */ ++ max_vlans = (MXL862XX_TOTAL_EVLAN_ENTRIES - ++ n_user_ports * ingress_finals) / ++ (n_user_ports * vid_rules); ++ priv->evlan_egress_size = vid_rules * max_vlans; ++ ++ /* VLAN Filter block: one per user port. The 1024-entry ++ * table is divided equally among user ports. Each port ++ * gets its own VF block for per-port VID membership -- ++ * discard_unmatched_tagged handles the rest. ++ */ ++ priv->vf_block_size = MXL862XX_TOTAL_VF_ENTRIES / n_user_ports; ++ } ++ + ret = mxl862xx_setup_drop_meter(ds); + if (ret) + return ret; +@@ -495,27 +803,616 @@ static int mxl862xx_configure_sp_tag_pro + return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag); + } + ++/** ++ * mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware ++ * @priv: driver private data ++ * @block_id: HW Extended VLAN block ID ++ * @entry_index: entry index within the block ++ * @desc: rule descriptor (filter type + action) ++ * @vid: VLAN ID for VID-specific rules (ignored when !desc->match_vid) ++ * @untagged: strip tag on egress for EVLAN_STRIP_IF_UNTAGGED action ++ * @pvid: port VLAN ID for PVID insertion rules (0 = no PVID) ++ * ++ * Translates a compact rule descriptor into a full firmware ++ * mxl862xx_extendedvlan_config struct and writes it via the API. ++ */ ++static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv, ++ u16 block_id, u16 entry_index, ++ const struct mxl862xx_evlan_rule_desc *desc, ++ u16 vid, bool untagged, u16 pvid) ++{ ++ struct mxl862xx_extendedvlan_config cfg = {}; ++ struct mxl862xx_extendedvlan_filter_vlan *fv; ++ ++ cfg.extended_vlan_block_id = cpu_to_le16(block_id); ++ cfg.entry_index = cpu_to_le16(entry_index); ++ ++ /* Populate filter */ ++ cfg.filter.outer_vlan.type = cpu_to_le32(desc->outer_type); ++ cfg.filter.inner_vlan.type = cpu_to_le32(desc->inner_type); ++ cfg.filter.outer_vlan.tpid = cpu_to_le32(desc->outer_tpid); ++ cfg.filter.inner_vlan.tpid = cpu_to_le32(desc->inner_tpid); ++ ++ if (desc->match_vid) { ++ /* For egress unaware: outer=NO_FILTER, match on inner tag */ ++ if (desc->outer_type == FT_NO_FILTER) ++ fv = &cfg.filter.inner_vlan; ++ else ++ fv = &cfg.filter.outer_vlan; ++ ++ fv->vid_enable = 1; ++ fv->vid_val = cpu_to_le32(vid); ++ } ++ ++ /* Populate treatment based on action */ ++ switch (desc->action) { ++ case EVLAN_ACCEPT: ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG); ++ break; ++ ++ case EVLAN_STRIP_IF_UNTAGGED: ++ cfg.treatment.remove_tag = cpu_to_le32(untagged ? ++ MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG : ++ MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG); ++ break; ++ ++ case EVLAN_DISCARD: ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM); ++ break; ++ ++ case EVLAN_PVID_OR_DISCARD: ++ if (pvid) { ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG); ++ cfg.treatment.add_outer_vlan = 1; ++ cfg.treatment.outer_vlan.vid_mode = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL); ++ cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid); ++ cfg.treatment.outer_vlan.tpid = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q); ++ } else { ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM); ++ } ++ break; ++ ++ case EVLAN_PVID_OR_PASS: ++ if (pvid) { ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG); ++ cfg.treatment.add_outer_vlan = 1; ++ cfg.treatment.outer_vlan.vid_mode = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL); ++ cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid); ++ cfg.treatment.outer_vlan.tpid = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q); ++ } else { ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG); ++ } ++ break; ++ ++ case EVLAN_STRIP1_AND_PVID_OR_DISCARD: ++ if (pvid) { ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG); ++ cfg.treatment.add_outer_vlan = 1; ++ cfg.treatment.outer_vlan.vid_mode = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL); ++ cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid); ++ cfg.treatment.outer_vlan.tpid = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q); ++ } else { ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM); ++ } ++ break; ++ } ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg); ++} ++ ++/** ++ * mxl862xx_evlan_deactivate_entry - Reset an Extended VLAN entry to no-op ++ * @priv: driver private data ++ * @block_id: HW Extended VLAN block ID ++ * @entry_index: entry index within the block ++ * ++ * Writes a zeroed-out config to the firmware, which deactivates the ++ * rule (making it transparent / no-op). ++ */ ++static int mxl862xx_evlan_deactivate_entry(struct mxl862xx_priv *priv, ++ u16 block_id, u16 entry_index) ++{ ++ struct mxl862xx_extendedvlan_config cfg = {}; ++ ++ cfg.extended_vlan_block_id = cpu_to_le16(block_id); ++ cfg.entry_index = cpu_to_le16(entry_index); ++ ++ /* Use an unreachable filter (DEFAULT+DEFAULT) with DISCARD treatment. ++ * A zeroed entry would have NORMAL+NORMAL filter which matches ++ * real double-tagged traffic and passes it through. ++ */ ++ cfg.filter.outer_vlan.type = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT); ++ cfg.filter.inner_vlan.type = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT); ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM); ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg); ++} ++ ++/** ++ * mxl862xx_evlan_write_final_rules - Write catchall rules to the ingress block ++ * @priv: driver private data ++ * @blk: Extended VLAN block (already allocated) ++ * @rules: array of rule descriptors for the final rules ++ * @n_rules: number of final rules ++ * @pvid: port VLAN ID (for PVID insertion rules) ++ * ++ * Writes final catchall rules starting at block_size - n_rules. With ++ * VLAN Filter handling VID membership, only the ingress block uses ++ * finals, and the block is sized to exactly fit them (no VID entries), ++ * so the rules fill the entire block. ++ */ ++static int mxl862xx_evlan_write_final_rules(struct mxl862xx_priv *priv, ++ struct mxl862xx_evlan_block *blk, ++ const struct mxl862xx_evlan_rule_desc *rules, ++ int n_rules, u16 pvid) ++{ ++ u16 start_idx = blk->block_size - n_rules; ++ int i, ret; ++ ++ for (i = 0; i < n_rules; i++) { ++ ret = mxl862xx_evlan_write_rule(priv, blk->block_id, ++ start_idx + i, &rules[i], ++ 0, false, pvid); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * mxl862xx_vf_entry_set - Write a single VLAN Filter entry ++ * @priv: driver private data ++ * @block_id: HW VLAN Filter block ID ++ * @index: entry index within the block ++ * @vid: VLAN ID to allow ++ * ++ * Writes an ALLOW entry (discard_matched=false) for the given VID. ++ */ ++static int mxl862xx_vf_entry_set(struct mxl862xx_priv *priv, ++ u16 block_id, u16 index, u16 vid) ++{ ++ struct mxl862xx_vlanfilter_config cfg = {}; ++ ++ cfg.vlan_filter_block_id = cpu_to_le16(block_id); ++ cfg.entry_index = cpu_to_le16(index); ++ cfg.vlan_filter_mask = cpu_to_le32(MXL862XX_VLAN_FILTER_TCI_MASK_VID); ++ cfg.val = cpu_to_le32(vid); ++ cfg.discard_matched = 0; ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg); ++} ++ ++/** ++ * mxl862xx_vf_find_vid - Find a VID entry in a VF block ++ * @vf: VLAN Filter block to search ++ * @vid: VLAN ID to find ++ */ ++static struct mxl862xx_vf_vid * ++mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf, u16 vid) ++{ ++ struct mxl862xx_vf_vid *ve; ++ ++ list_for_each_entry(ve, &vf->vids, list) ++ if (ve->vid == vid) ++ return ve; ++ ++ return NULL; ++} ++ ++/** ++ * mxl862xx_vf_add_vid - Add a VID to a port's VLAN Filter block ++ * @priv: driver private data ++ * @vf: VLAN Filter block ++ * @vid: VLAN ID to add ++ * @untagged: whether this VID should strip tags on egress ++ * ++ * Idempotent. Writes an ALLOW entry at active_count and increments ++ * active_count. If the VID already exists, only the untagged flag ++ * is updated. The HW block must be allocated before calling this. ++ */ ++static int mxl862xx_vf_add_vid(struct mxl862xx_priv *priv, ++ struct mxl862xx_vf_block *vf, ++ u16 vid, bool untagged) ++{ ++ struct mxl862xx_vf_vid *ve; ++ int ret; ++ ++ ve = mxl862xx_vf_find_vid(vf, vid); ++ if (ve) { ++ ve->untagged = untagged; ++ return 0; ++ } ++ ++ if (vf->active_count >= vf->block_size) ++ return -ENOSPC; ++ ++ ve = kzalloc(sizeof(*ve), GFP_KERNEL); ++ if (!ve) ++ return -ENOMEM; ++ ++ ve->vid = vid; ++ ve->index = vf->active_count; ++ ve->untagged = untagged; ++ ++ ret = mxl862xx_vf_entry_set(priv, vf->block_id, ve->index, vid); ++ if (ret) { ++ kfree(ve); ++ return ret; ++ } ++ ++ list_add_tail(&ve->list, &vf->vids); ++ vf->active_count++; ++ ++ return 0; ++} ++ ++/** ++ * mxl862xx_vf_del_vid - Remove a VID from a port's VLAN Filter block ++ * @priv: driver private data ++ * @vf: VLAN Filter block ++ * @vid: VLAN ID to remove ++ * ++ * Swap-compacts: the last active entry is moved into the gap, ++ * active_count is decremented, and the old last slot is plugged ++ * with DISCARD. When active_count drops to 0, a DISCARD sentinel ++ * is restored at index 0. ++ */ ++static int mxl862xx_vf_del_vid(struct mxl862xx_priv *priv, ++ struct mxl862xx_vf_block *vf, u16 vid) ++{ ++ struct mxl862xx_vf_vid *ve, *last_ve; ++ u16 gap, last; ++ int ret; ++ ++ ve = mxl862xx_vf_find_vid(vf, vid); ++ if (!ve) ++ return 0; ++ ++ if (!vf->allocated) { ++ /* Software-only state -- just remove the tracking entry */ ++ list_del(&ve->list); ++ kfree(ve); ++ vf->active_count--; ++ return 0; ++ } ++ ++ gap = ve->index; ++ last = vf->active_count - 1; ++ ++ if (vf->active_count == 1) { ++ /* Last VID -- restore DISCARD sentinel at index 0 */ ++ ret = mxl862xx_vf_entry_discard(priv, vf->block_id, 0); ++ if (ret) ++ return ret; ++ } else if (gap < last) { ++ /* Swap: move the last ALLOW entry into the gap */ ++ last_ve = NULL; ++ list_for_each_entry(last_ve, &vf->vids, list) ++ if (last_ve->index == last) ++ break; ++ ++ if (WARN_ON(!last_ve || last_ve->index != last)) ++ return -EINVAL; ++ ++ ret = mxl862xx_vf_entry_set(priv, vf->block_id, ++ gap, last_ve->vid); ++ if (ret) ++ return ret; ++ ++ last_ve->index = gap; ++ ++ /* Plug the old last slot with DISCARD */ ++ ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last); ++ if (ret) ++ return ret; ++ } else { ++ /* Deleting the last entry -- just plug it */ ++ ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last); ++ if (ret) ++ return ret; ++ } ++ ++ list_del(&ve->list); ++ kfree(ve); ++ vf->active_count--; ++ ++ return 0; ++} ++ ++/** ++ * mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules ++ * @priv: driver private data ++ * @port: port number ++ * ++ * In VLAN-aware mode the ingress EVLAN block handles PVID insertion for ++ * untagged/priority-tagged frames, passes through standard 802.1Q ++ * tagged frames for VF membership checking, and treats non-8021Q TPID ++ * frames as untagged. The block is sized to exactly fit the 7 catchall ++ * rules and is rewritten whenever PVID changes. ++ * ++ * In VLAN-unaware mode the firmware passes frames through unchanged when ++ * no ingress block is assigned, so nothing is programmed. ++ */ ++static int mxl862xx_evlan_program_ingress(struct mxl862xx_priv *priv, int port) ++{ ++ struct mxl862xx_port *p = &priv->ports[port]; ++ struct mxl862xx_evlan_block *blk = &p->ingress_evlan; ++ ++ if (!p->vlan_filtering) ++ return 0; ++ ++ blk->in_use = true; ++ blk->n_active = blk->block_size; ++ ++ return mxl862xx_evlan_write_final_rules(priv, blk, ++ ingress_aware_final, ++ ARRAY_SIZE(ingress_aware_final), ++ p->pvid); ++} ++ ++/** ++ * mxl862xx_evlan_program_egress - Reprogram all egress tag-stripping rules ++ * @priv: driver private data ++ * @port: port number ++ * ++ * Walks the port's VF VID list and writes 2 EVLAN rules per VID that ++ * needs egress tag stripping. In VLAN-aware mode only untagged VIDs ++ * need rules (tagged VIDs pass through EVLAN untouched). In unaware ++ * mode every VID gets rules. ++ * ++ * Entries are packed starting at index 0, and the scan window ++ * (n_active) is narrowed so stale entries beyond it are never matched. ++ */ ++static int mxl862xx_evlan_program_egress(struct mxl862xx_priv *priv, int port) ++{ ++ struct mxl862xx_port *p = &priv->ports[port]; ++ struct mxl862xx_evlan_block *blk = &p->egress_evlan; ++ const struct mxl862xx_evlan_rule_desc *vid_rules; ++ struct mxl862xx_vf_vid *vfv; ++ u16 old_active = blk->n_active; ++ u16 idx = 0, i; ++ int n_vid, ret; ++ ++ if (p->vlan_filtering) { ++ vid_rules = vid_accept_standard; ++ n_vid = ARRAY_SIZE(vid_accept_standard); ++ } else { ++ vid_rules = vid_accept_egress_unaware; ++ n_vid = ARRAY_SIZE(vid_accept_egress_unaware); ++ } ++ ++ list_for_each_entry(vfv, &p->vf.vids, list) { ++ /* In VLAN-aware mode tagged-only VIDs need no EVLAN ++ * rules -- VLAN Filter handles membership. ++ */ ++ if (p->vlan_filtering && !vfv->untagged) ++ continue; ++ ++ if (idx + n_vid > blk->block_size) ++ return -ENOSPC; ++ ++ ret = mxl862xx_evlan_write_rule(priv, blk->block_id, ++ idx++, &vid_rules[0], ++ vfv->vid, vfv->untagged, ++ p->pvid); ++ if (ret) ++ return ret; ++ ++ if (n_vid > 1) { ++ ret = mxl862xx_evlan_write_rule(priv, blk->block_id, ++ idx++, &vid_rules[1], ++ vfv->vid, ++ vfv->untagged, ++ p->pvid); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ /* Deactivate stale entries that are no longer needed. ++ * This closes the brief window between writing the new rules ++ * and set_bridge_port narrowing the scan window. ++ */ ++ for (i = idx; i < old_active; i++) { ++ ret = mxl862xx_evlan_deactivate_entry(priv, ++ blk->block_id, ++ i); ++ if (ret) ++ return ret; ++ } ++ ++ blk->n_active = idx; ++ blk->in_use = idx > 0; ++ ++ return 0; ++} ++ ++static int mxl862xx_port_vlan_filtering(struct dsa_switch *ds, int port, ++ bool vlan_filtering, ++ struct netlink_ext_ack *extack) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ bool changed = (p->vlan_filtering != vlan_filtering); ++ int ret; ++ ++ p->vlan_filtering = vlan_filtering; ++ ++ /* Reprogram Extended VLAN rules if filtering mode changed */ ++ if (changed) { ++ /* When leaving VLAN-aware mode, release the ingress HW ++ * block. The firmware passes frames through unchanged ++ * when no ingress EVLAN block is assigned, so the block ++ * is unnecessary in unaware mode. ++ */ ++ if (!vlan_filtering) ++ p->ingress_evlan.in_use = false; ++ ++ ret = mxl862xx_evlan_program_ingress(priv, port); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_evlan_program_egress(priv, port); ++ if (ret) ++ return ret; ++ } ++ ++ /* Push VLAN-based MAC learning flags and (possibly newly ++ * allocated) ingress block to hardware. ++ */ ++ return mxl862xx_set_bridge_port(ds, port); ++} ++ ++static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct netlink_ext_ack *extack) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); ++ u16 vid = vlan->vid; ++ u16 old_pvid = p->pvid; ++ bool pvid_changed = false; ++ int ret; ++ ++ /* CPU port is VLAN-transparent: the SP tag handles port ++ * identification and the host-side DSA tagger manages VLAN ++ * delivery. Egress EVLAN catchalls are set up once in ++ * setup_cpu_bridge; no per-VID VF/EVLAN programming needed. ++ */ ++ if (dsa_is_cpu_port(ds, port)) ++ return 0; ++ ++ /* Update PVID tracking */ ++ if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { ++ if (p->pvid != vid) { ++ p->pvid = vid; ++ pvid_changed = true; ++ } ++ } else if (p->pvid == vid) { ++ p->pvid = 0; ++ pvid_changed = true; ++ } ++ ++ /* Add/update VID in this port's VLAN Filter block. ++ * VF must be updated before programming egress EVLAN because ++ * evlan_program_egress walks the VF VID list. ++ */ ++ ret = mxl862xx_vf_add_vid(priv, &p->vf, vid, untagged); ++ if (ret) ++ goto err_pvid; ++ ++ /* Reprogram ingress finals if PVID changed */ ++ if (pvid_changed) { ++ ret = mxl862xx_evlan_program_ingress(priv, port); ++ if (ret) ++ goto err_pvid; ++ } ++ ++ /* Reprogram egress tag-stripping rules (walks VF VID list) */ ++ ret = mxl862xx_evlan_program_egress(priv, port); ++ if (ret) ++ goto err_pvid; ++ ++ /* Apply VLAN block IDs and MAC learning flags to bridge port */ ++ ret = mxl862xx_set_bridge_port(ds, port); ++ if (ret) ++ goto err_pvid; ++ ++ return 0; ++ ++err_pvid: ++ p->pvid = old_pvid; ++ return ret; ++} ++ ++static int mxl862xx_port_vlan_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ u16 vid = vlan->vid; ++ bool pvid_changed = false; ++ int ret; ++ ++ if (dsa_is_cpu_port(ds, port)) ++ return 0; ++ ++ /* Clear PVID if we're deleting it */ ++ if (p->pvid == vid) { ++ p->pvid = 0; ++ pvid_changed = true; ++ } ++ ++ /* Remove VID from this port's VLAN Filter block. ++ * Must happen before egress reprogram so the VID is no ++ * longer in the list that evlan_program_egress walks. ++ */ ++ ret = mxl862xx_vf_del_vid(priv, &p->vf, vid); ++ if (ret) ++ return ret; ++ ++ /* Reprogram egress tag-stripping rules (VID is now gone) */ ++ ret = mxl862xx_evlan_program_egress(priv, port); ++ if (ret) ++ return ret; ++ ++ /* If PVID changed, reprogram ingress finals */ ++ if (pvid_changed) { ++ ret = mxl862xx_evlan_program_ingress(priv, port); ++ if (ret) ++ return ret; ++ } ++ ++ return mxl862xx_set_bridge_port(ds, port); ++} ++ + static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port) + { + struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; + struct dsa_port *dp; + +- priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE; +- priv->ports[port].learning = true; ++ p->fid = MXL862XX_DEFAULT_BRIDGE; ++ p->learning = true; ++ ++ /* EVLAN is left disabled on CPU ports -- frames pass through ++ * without EVLAN processing. Only the portmap and bridge ++ * assignment need to be configured. ++ */ + + /* include all assigned user ports in the CPU portmap */ +- bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS); ++ bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS); + dsa_switch_for_each_user_port(dp, ds) { + /* it's safe to rely on cpu_dp being valid for user ports */ + if (dp->cpu_dp->index != port) + continue; + +- __set_bit(dp->index, priv->ports[port].portmap); ++ __set_bit(dp->index, p->portmap); + } + + return mxl862xx_set_bridge_port(ds, port); + } + ++ + static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port, + const struct dsa_bridge bridge, + bool *tx_fwd_offload, +@@ -565,6 +1462,22 @@ static void mxl862xx_port_bridge_leave(s + bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS); + __set_bit(dp->cpu_dp->index, p->portmap); + p->flood_block = 0; ++ ++ /* Detach EVLAN and VF blocks from the bridge port BEFORE freeing ++ * them. The firmware tracks a usage count per block and rejects ++ * FREE while the count is non-zero. ++ * ++ * For EVLAN: setting in_use=false makes set_bridge_port send ++ * enable=false, which decrements the firmware refcount. ++ * ++ * For VF: set_bridge_port sees dp->bridge == NULL (DSA already ++ * cleared it) and sends vlan_filter_enable=0, which decrements ++ * the firmware VF refcount. ++ */ ++ p->pvid = 0; ++ p->ingress_evlan.in_use = false; ++ p->egress_evlan.in_use = false; ++ + err = mxl862xx_set_bridge_port(ds, port); + if (err) + dev_err(ds->dev, +@@ -614,6 +1527,28 @@ static int mxl862xx_port_setup(struct ds + if (ret) + return ret; + ++ /* Initialize and pre-allocate per-port EVLAN and VF blocks for ++ * user ports. CPU ports do not use EVLAN or VF -- frames pass ++ * through without processing. Pre-allocation avoids firmware ++ * EVLAN table fragmentation and simplifies control flow. ++ */ ++ mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan, ++ priv->evlan_ingress_size); ++ ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan); ++ if (ret) ++ return ret; ++ ++ mxl862xx_evlan_block_init(&priv->ports[port].egress_evlan, ++ priv->evlan_egress_size); ++ ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].egress_evlan); ++ if (ret) ++ return ret; ++ ++ mxl862xx_vf_init(&priv->ports[port].vf, priv->vf_block_size); ++ ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf); ++ if (ret) ++ return ret; ++ + priv->ports[port].setup_done = true; + + return 0; +@@ -808,7 +1743,7 @@ static int mxl862xx_port_mdb_del(struct + mxl862xx_fw_portmap_clear_bit(qparam.port_map, port); + + if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) { +- /* No ports left — remove the entry entirely */ ++ /* No ports left -- remove the entry entirely */ + rparam.fid = cpu_to_le16(fid); + rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); + ether_addr_copy(rparam.mac, mdb->addr); +@@ -899,7 +1834,7 @@ static void mxl862xx_port_stp_state_set( + /* Deferred work handler for host flood configuration. + * + * port_set_host_flood is called from atomic context (under +- * netif_addr_lock), so firmware calls must be deferred. The worker ++ * netif_addr_lock), so firmware calls must be deferred. The worker + * acquires rtnl_lock() to serialize with DSA callbacks that access the + * same driver state. + */ +@@ -924,9 +1859,9 @@ static void mxl862xx_host_flood_work_fn( + mc = p->host_flood_mc; + + /* The hardware controls unknown-unicast/multicast forwarding per FID +- * (bridge), not per source port. For bridged ports all members share ++ * (bridge), not per source port. For bridged ports all members share + * one FID, so we cannot selectively suppress flooding to the CPU for +- * one source port while allowing it for another. Silently ignore the ++ * one source port while allowing it for another. Silently ignore the + * request -- the excess flooding towards the CPU is harmless. + */ + if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port))) +@@ -1026,6 +1961,9 @@ static const struct dsa_switch_ops mxl86 + .port_fdb_dump = mxl862xx_port_fdb_dump, + .port_mdb_add = mxl862xx_port_mdb_add, + .port_mdb_del = mxl862xx_port_mdb_del, ++ .port_vlan_filtering = mxl862xx_port_vlan_filtering, ++ .port_vlan_add = mxl862xx_port_vlan_add, ++ .port_vlan_del = mxl862xx_port_vlan_del, + }; + + static void mxl862xx_phylink_mac_config(struct phylink_config *config, +@@ -1111,7 +2049,7 @@ static void mxl862xx_remove(struct mdio_ + + /* Cancel any pending host flood work. dsa_unregister_switch() + * has already called port_teardown (which sets setup_done=false), +- * but a worker could still be blocked on rtnl_lock(). Since we ++ * but a worker could still be blocked on rtnl_lock(). Since we + * are now outside RTNL, cancel_work_sync() will not deadlock. + */ + for (i = 0; i < MXL862XX_MAX_PORTS; i++) +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -13,6 +13,8 @@ struct mxl862xx_priv; + #define MXL862XX_DEFAULT_BRIDGE 0 + #define MXL862XX_MAX_BRIDGES 48 + #define MXL862XX_MAX_BRIDGE_PORTS 128 ++#define MXL862XX_TOTAL_EVLAN_ENTRIES 1024 ++#define MXL862XX_TOTAL_VF_ENTRIES 1024 + + /* Number of __le16 words in a firmware portmap (128-bit bitmap). */ + #define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16) +@@ -86,12 +88,72 @@ static inline bool mxl862xx_fw_portmap_i + } + + /** ++ * struct mxl862xx_vf_vid - Per-VID entry within a VLAN Filter block ++ * @list: Linked into &mxl862xx_vf_block.vids ++ * @vid: VLAN ID ++ * @index: Entry index within the VLAN Filter HW block ++ * @untagged: Strip tag on egress for this VID (drives EVLAN tag-stripping) ++ */ ++struct mxl862xx_vf_vid { ++ struct list_head list; ++ u16 vid; ++ u16 index; ++ bool untagged; ++}; ++ ++/** ++ * struct mxl862xx_vf_block - Per-port VLAN Filter block ++ * @allocated: Whether the HW block has been allocated via VLANFILTER_ALLOC ++ * @block_id: HW VLAN Filter block ID from VLANFILTER_ALLOC ++ * @block_size: Total entries allocated in this block ++ * @active_count: Number of ALLOW entries at indices [0, active_count). ++ * The bridge port config sends max(active_count, 1) as ++ * block_size to narrow the HW scan window. ++ * discard_unmatched_tagged handles frames outside this range. ++ * @vids: List of &mxl862xx_vf_vid entries programmed in this block ++ */ ++struct mxl862xx_vf_block { ++ bool allocated; ++ u16 block_id; ++ u16 block_size; ++ u16 active_count; ++ struct list_head vids; ++}; ++ ++/** ++ * struct mxl862xx_evlan_block - Per-port per-direction extended VLAN block ++ * @allocated: Whether the HW block has been allocated via EXTENDEDVLAN_ALLOC. ++ * Guards alloc/free idempotency--the block_id is only valid ++ * while allocated is true. ++ * @in_use: Whether the EVLAN engine should be enabled for this block ++ * on the bridge port (sent as the enable flag in ++ * set_bridge_port). Can be false while allocated is still ++ * true -- e.g. when all egress VIDs are removed (idx == 0 in ++ * evlan_program_egress) the block stays allocated for ++ * potential reuse, but the engine is disabled so an empty ++ * rule set does not discard all traffic. ++ * @block_id: HW block ID from EXTENDEDVLAN_ALLOC ++ * @block_size: Total entries allocated ++ * @n_active: Number of HW entries currently written. The bridge port ++ * config sends this as the egress scan window, so entries ++ * beyond n_active are never scanned. Always equals ++ * block_size for ingress blocks (fixed catchall rules). ++ */ ++struct mxl862xx_evlan_block { ++ bool allocated; ++ bool in_use; ++ u16 block_id; ++ u16 block_size; ++ u16 n_active; ++}; ++ ++/** + * struct mxl862xx_port - per-port state tracked by the driver + * @priv: back-pointer to switch private data; needed by + * deferred work handlers to access ds and priv +- * @fid: firmware FID for the permanent single-port bridge; +- * kept alive for the lifetime of the port so traffic is +- * never forwarded while the port is unbridged ++ * @fid: firmware FID for the permanent single-port bridge; kept ++ * alive for the lifetime of the port so traffic is never ++ * forwarded while the port is unbridged + * @portmap: bitmap of switch port indices that share the current + * bridge with this port + * @flood_block: bitmask of firmware meter indices that are currently +@@ -101,6 +163,11 @@ static inline bool mxl862xx_fw_portmap_i + * @setup_done: set at end of port_setup, cleared at start of + * port_teardown; guards deferred work against + * acting on torn-down state ++ * @pvid: port VLAN ID (native VLAN) assigned to untagged traffic ++ * @vlan_filtering: true when VLAN filtering is enabled on this port ++ * @vf: per-port VLAN Filter block state ++ * @ingress_evlan: ingress extended VLAN block state ++ * @egress_evlan: egress extended VLAN block state + * @host_flood_uc: desired host unicast flood state (true = flood); + * updated atomically by port_set_host_flood, consumed + * by the deferred host_flood_work +@@ -119,6 +186,12 @@ struct mxl862xx_port { + unsigned long flood_block; + bool learning; + bool setup_done; ++ /* VLAN state */ ++ u16 pvid; ++ bool vlan_filtering; ++ struct mxl862xx_vf_block vf; ++ struct mxl862xx_evlan_block ingress_evlan; ++ struct mxl862xx_evlan_block egress_evlan; + bool host_flood_uc; + bool host_flood_mc; + struct work_struct host_flood_work; +@@ -126,17 +199,23 @@ struct mxl862xx_port { + + /** + * struct mxl862xx_priv - driver private data for an MxL862xx switch +- * @ds: pointer to the DSA switch instance +- * @mdiodev: MDIO device used to communicate with the switch firmware +- * @crc_err_work: deferred work for shutting down all ports on MDIO CRC errors +- * @crc_err: set atomically before CRC-triggered shutdown, cleared after +- * @drop_meter: index of the single shared zero-rate firmware meter used +- * to unconditionally drop traffic (used to block flooding) +- * @ports: per-port state, indexed by switch port number +- * @bridges: maps DSA bridge number to firmware bridge ID; +- * zero means no firmware bridge allocated for that +- * DSA bridge number. Indexed by dsa_bridge.num +- * (0 .. ds->max_num_bridges). ++ * @ds: pointer to the DSA switch instance ++ * @mdiodev: MDIO device used to communicate with the switch firmware ++ * @crc_err_work: deferred work for shutting down all ports on MDIO CRC ++ * errors ++ * @crc_err: set atomically before CRC-triggered shutdown, cleared ++ * after ++ * @drop_meter: index of the single shared zero-rate firmware meter ++ * used to unconditionally drop traffic (used to block ++ * flooding) ++ * @ports: per-port state, indexed by switch port number ++ * @bridges: maps DSA bridge number to firmware bridge ID; ++ * zero means no firmware bridge allocated for that ++ * DSA bridge number. Indexed by dsa_bridge.num ++ * (0 .. ds->max_num_bridges). ++ * @evlan_ingress_size: per-port ingress Extended VLAN block size ++ * @evlan_egress_size: per-port egress Extended VLAN block size ++ * @vf_block_size: per-port VLAN Filter block size + */ + struct mxl862xx_priv { + struct dsa_switch *ds; +@@ -146,6 +225,9 @@ struct mxl862xx_priv { + u16 drop_meter; + struct mxl862xx_port ports[MXL862XX_MAX_PORTS]; + u16 bridges[MXL862XX_MAX_BRIDGES + 1]; ++ u16 evlan_ingress_size; ++ u16 evlan_egress_size; ++ u16 vf_block_size; + }; + + #endif /* __MXL862XX_H */ diff --git a/target/linux/generic/pending-6.12/760-06-net-dsa-mxl862xx-add-ethtool-statistics-support.patch b/target/linux/generic/pending-6.12/760-06-net-dsa-mxl862xx-add-ethtool-statistics-support.patch new file mode 100644 index 00000000000..a1517ccde0a --- /dev/null +++ b/target/linux/generic/pending-6.12/760-06-net-dsa-mxl862xx-add-ethtool-statistics-support.patch @@ -0,0 +1,383 @@ +From 03b583e774835f771dd7c3c265be5903f008e8e5 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 22 Mar 2026 00:57:33 +0000 +Subject: [PATCH 15/35] net: dsa: mxl862xx: add ethtool statistics support + +The MxL862xx firmware exposes per-port RMON counters through the +RMON_PORT_GET command, covering standard IEEE 802.3 MAC statistics +(unicast/multicast/broadcast packet and byte counts, collision +counters, pause frames) as well as hardware-specific counters such +as extended VLAN discard and MTU exceed events. + +Add the RMON counter firmware API structures and command definitions. +Implement .get_strings, .get_sset_count, and .get_ethtool_stats for +legacy ethtool -S support. Implement .get_eth_mac_stats, +.get_eth_ctrl_stats, and .get_pause_stats for the standardized +IEEE 802.3 statistics interface. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 142 ++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 3 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 168 ++++++++++++++++++++++++ + 3 files changed, 313 insertions(+) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -1224,4 +1224,146 @@ struct mxl862xx_sys_fw_image_version { + __le32 iv_build_num; + } __packed; + ++/** ++ * enum mxl862xx_port_type - Port Type ++ * @MXL862XX_LOGICAL_PORT: Logical Port ++ * @MXL862XX_PHYSICAL_PORT: Physical Port ++ * @MXL862XX_CTP_PORT: Connectivity Termination Port (CTP) ++ * @MXL862XX_BRIDGE_PORT: Bridge Port ++ */ ++enum mxl862xx_port_type { ++ MXL862XX_LOGICAL_PORT = 0, ++ MXL862XX_PHYSICAL_PORT, ++ MXL862XX_CTP_PORT, ++ MXL862XX_BRIDGE_PORT, ++}; ++ ++/** ++ * enum mxl862xx_rmon_port_type - RMON counter table type ++ * @MXL862XX_RMON_CTP_PORT_RX: CTP RX counters ++ * @MXL862XX_RMON_CTP_PORT_TX: CTP TX counters ++ * @MXL862XX_RMON_BRIDGE_PORT_RX: Bridge port RX counters ++ * @MXL862XX_RMON_BRIDGE_PORT_TX: Bridge port TX counters ++ * @MXL862XX_RMON_CTP_PORT_PCE_BYPASS: CTP PCE bypass counters ++ * @MXL862XX_RMON_TFLOW_RX: TFLOW RX counters ++ * @MXL862XX_RMON_TFLOW_TX: TFLOW TX counters ++ * @MXL862XX_RMON_QMAP: QMAP counters ++ * @MXL862XX_RMON_METER: Meter counters ++ * @MXL862XX_RMON_PMAC: PMAC counters ++ */ ++enum mxl862xx_rmon_port_type { ++ MXL862XX_RMON_CTP_PORT_RX = 0, ++ MXL862XX_RMON_CTP_PORT_TX, ++ MXL862XX_RMON_BRIDGE_PORT_RX, ++ MXL862XX_RMON_BRIDGE_PORT_TX, ++ MXL862XX_RMON_CTP_PORT_PCE_BYPASS, ++ MXL862XX_RMON_TFLOW_RX, ++ MXL862XX_RMON_TFLOW_TX, ++ MXL862XX_RMON_QMAP = 0x0e, ++ MXL862XX_RMON_METER = 0x19, ++ MXL862XX_RMON_PMAC = 0x1c, ++}; ++ ++/** ++ * struct mxl862xx_rmon_port_cnt - RMON counters for a port ++ * @port_type: Port type for counter retrieval (see &enum mxl862xx_port_type) ++ * @port_id: Ethernet port number (zero-based) ++ * @sub_if_id_group: Sub-interface ID group ++ * @pce_bypass: Separate CTP Tx counters when PCE is bypassed ++ * @rx_extended_vlan_discard_pkts: Discarded at extended VLAN operation ++ * @mtu_exceed_discard_pkts: Discarded due to MTU exceeded ++ * @tx_under_size_good_pkts: Tx undersize (<64) packet count ++ * @tx_oversize_good_pkts: Tx oversize (>1518) packet count ++ * @rx_good_pkts: Received good packet count ++ * @rx_unicast_pkts: Received unicast packet count ++ * @rx_broadcast_pkts: Received broadcast packet count ++ * @rx_multicast_pkts: Received multicast packet count ++ * @rx_fcserror_pkts: Received FCS error packet count ++ * @rx_under_size_good_pkts: Received undersize good packet count ++ * @rx_oversize_good_pkts: Received oversize good packet count ++ * @rx_under_size_error_pkts: Received undersize error packet count ++ * @rx_good_pause_pkts: Received good pause packet count ++ * @rx_oversize_error_pkts: Received oversize error packet count ++ * @rx_align_error_pkts: Received alignment error packet count ++ * @rx_filtered_pkts: Filtered packet count ++ * @rx64byte_pkts: Received 64-byte packet count ++ * @rx127byte_pkts: Received 65-127 byte packet count ++ * @rx255byte_pkts: Received 128-255 byte packet count ++ * @rx511byte_pkts: Received 256-511 byte packet count ++ * @rx1023byte_pkts: Received 512-1023 byte packet count ++ * @rx_max_byte_pkts: Received 1024-max byte packet count ++ * @tx_good_pkts: Transmitted good packet count ++ * @tx_unicast_pkts: Transmitted unicast packet count ++ * @tx_broadcast_pkts: Transmitted broadcast packet count ++ * @tx_multicast_pkts: Transmitted multicast packet count ++ * @tx_single_coll_count: Transmit single collision count ++ * @tx_mult_coll_count: Transmit multiple collision count ++ * @tx_late_coll_count: Transmit late collision count ++ * @tx_excess_coll_count: Transmit excessive collision count ++ * @tx_coll_count: Transmit collision count ++ * @tx_pause_count: Transmit pause packet count ++ * @tx64byte_pkts: Transmitted 64-byte packet count ++ * @tx127byte_pkts: Transmitted 65-127 byte packet count ++ * @tx255byte_pkts: Transmitted 128-255 byte packet count ++ * @tx511byte_pkts: Transmitted 256-511 byte packet count ++ * @tx1023byte_pkts: Transmitted 512-1023 byte packet count ++ * @tx_max_byte_pkts: Transmitted 1024-max byte packet count ++ * @tx_dropped_pkts: Transmit dropped packet count ++ * @tx_acm_dropped_pkts: Transmit ACM dropped packet count ++ * @rx_dropped_pkts: Received dropped packet count ++ * @rx_good_bytes: Received good byte count (64-bit) ++ * @rx_bad_bytes: Received bad byte count (64-bit) ++ * @tx_good_bytes: Transmitted good byte count (64-bit) ++ */ ++struct mxl862xx_rmon_port_cnt { ++ enum mxl862xx_port_type port_type; ++ __le16 port_id; ++ __le16 sub_if_id_group; ++ u8 pce_bypass; ++ __le32 rx_extended_vlan_discard_pkts; ++ __le32 mtu_exceed_discard_pkts; ++ __le32 tx_under_size_good_pkts; ++ __le32 tx_oversize_good_pkts; ++ __le32 rx_good_pkts; ++ __le32 rx_unicast_pkts; ++ __le32 rx_broadcast_pkts; ++ __le32 rx_multicast_pkts; ++ __le32 rx_fcserror_pkts; ++ __le32 rx_under_size_good_pkts; ++ __le32 rx_oversize_good_pkts; ++ __le32 rx_under_size_error_pkts; ++ __le32 rx_good_pause_pkts; ++ __le32 rx_oversize_error_pkts; ++ __le32 rx_align_error_pkts; ++ __le32 rx_filtered_pkts; ++ __le32 rx64byte_pkts; ++ __le32 rx127byte_pkts; ++ __le32 rx255byte_pkts; ++ __le32 rx511byte_pkts; ++ __le32 rx1023byte_pkts; ++ __le32 rx_max_byte_pkts; ++ __le32 tx_good_pkts; ++ __le32 tx_unicast_pkts; ++ __le32 tx_broadcast_pkts; ++ __le32 tx_multicast_pkts; ++ __le32 tx_single_coll_count; ++ __le32 tx_mult_coll_count; ++ __le32 tx_late_coll_count; ++ __le32 tx_excess_coll_count; ++ __le32 tx_coll_count; ++ __le32 tx_pause_count; ++ __le32 tx64byte_pkts; ++ __le32 tx127byte_pkts; ++ __le32 tx255byte_pkts; ++ __le32 tx511byte_pkts; ++ __le32 tx1023byte_pkts; ++ __le32 tx_max_byte_pkts; ++ __le32 tx_dropped_pkts; ++ __le32 tx_acm_dropped_pkts; ++ __le32 rx_dropped_pkts; ++ __le64 rx_good_bytes; ++ __le64 rx_bad_bytes; ++ __le64 tx_good_bytes; ++} __packed; ++ + #endif /* __MXL862XX_API_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -16,6 +16,7 @@ + #define MXL862XX_BRDGPORT_MAGIC 0x400 + #define MXL862XX_CTP_MAGIC 0x500 + #define MXL862XX_QOS_MAGIC 0x600 ++#define MXL862XX_RMON_MAGIC 0x700 + #define MXL862XX_SWMAC_MAGIC 0xa00 + #define MXL862XX_EXTVLAN_MAGIC 0xb00 + #define MXL862XX_VLANFILTER_MAGIC 0xc00 +@@ -43,6 +44,8 @@ + #define MXL862XX_QOS_METERCFGSET (MXL862XX_QOS_MAGIC + 0x2) + #define MXL862XX_QOS_METERALLOC (MXL862XX_QOS_MAGIC + 0x2a) + ++#define MXL862XX_RMON_PORT_GET (MXL862XX_RMON_MAGIC + 0x1) ++ + #define MXL862XX_MAC_TABLEENTRYADD (MXL862XX_SWMAC_MAGIC + 0x2) + #define MXL862XX_MAC_TABLEENTRYREAD (MXL862XX_SWMAC_MAGIC + 0x3) + #define MXL862XX_MAC_TABLEENTRYQUERY (MXL862XX_SWMAC_MAGIC + 0x4) +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -30,6 +30,64 @@ + #define MXL862XX_API_READ_QUIET(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) + ++struct mxl862xx_mib_desc { ++ unsigned int size; ++ unsigned int offset; ++ const char *name; ++}; ++ ++#define MIB_DESC(_size, _name, _element) \ ++{ \ ++ .size = _size, \ ++ .name = _name, \ ++ .offset = offsetof(struct mxl862xx_rmon_port_cnt, _element) \ ++} ++ ++static const struct mxl862xx_mib_desc mxl862xx_mib[] = { ++ MIB_DESC(1, "TxGoodPkts", tx_good_pkts), ++ MIB_DESC(1, "TxUnicastPkts", tx_unicast_pkts), ++ MIB_DESC(1, "TxBroadcastPkts", tx_broadcast_pkts), ++ MIB_DESC(1, "TxMulticastPkts", tx_multicast_pkts), ++ MIB_DESC(1, "Tx64BytePkts", tx64byte_pkts), ++ MIB_DESC(1, "Tx127BytePkts", tx127byte_pkts), ++ MIB_DESC(1, "Tx255BytePkts", tx255byte_pkts), ++ MIB_DESC(1, "Tx511BytePkts", tx511byte_pkts), ++ MIB_DESC(1, "Tx1023BytePkts", tx1023byte_pkts), ++ MIB_DESC(1, "TxMaxBytePkts", tx_max_byte_pkts), ++ MIB_DESC(1, "TxDroppedPkts", tx_dropped_pkts), ++ MIB_DESC(1, "TxAcmDroppedPkts", tx_acm_dropped_pkts), ++ MIB_DESC(2, "TxGoodBytes", tx_good_bytes), ++ MIB_DESC(1, "TxSingleCollCount", tx_single_coll_count), ++ MIB_DESC(1, "TxMultCollCount", tx_mult_coll_count), ++ MIB_DESC(1, "TxLateCollCount", tx_late_coll_count), ++ MIB_DESC(1, "TxExcessCollCount", tx_excess_coll_count), ++ MIB_DESC(1, "TxCollCount", tx_coll_count), ++ MIB_DESC(1, "TxPauseCount", tx_pause_count), ++ MIB_DESC(1, "RxGoodPkts", rx_good_pkts), ++ MIB_DESC(1, "RxUnicastPkts", rx_unicast_pkts), ++ MIB_DESC(1, "RxBroadcastPkts", rx_broadcast_pkts), ++ MIB_DESC(1, "RxMulticastPkts", rx_multicast_pkts), ++ MIB_DESC(1, "RxFCSErrorPkts", rx_fcserror_pkts), ++ MIB_DESC(1, "RxUnderSizeGoodPkts", rx_under_size_good_pkts), ++ MIB_DESC(1, "RxOversizeGoodPkts", rx_oversize_good_pkts), ++ MIB_DESC(1, "RxUnderSizeErrorPkts", rx_under_size_error_pkts), ++ MIB_DESC(1, "RxOversizeErrorPkts", rx_oversize_error_pkts), ++ MIB_DESC(1, "RxFilteredPkts", rx_filtered_pkts), ++ MIB_DESC(1, "Rx64BytePkts", rx64byte_pkts), ++ MIB_DESC(1, "Rx127BytePkts", rx127byte_pkts), ++ MIB_DESC(1, "Rx255BytePkts", rx255byte_pkts), ++ MIB_DESC(1, "Rx511BytePkts", rx511byte_pkts), ++ MIB_DESC(1, "Rx1023BytePkts", rx1023byte_pkts), ++ MIB_DESC(1, "RxMaxBytePkts", rx_max_byte_pkts), ++ MIB_DESC(1, "RxDroppedPkts", rx_dropped_pkts), ++ MIB_DESC(1, "RxExtendedVlanDiscardPkts", rx_extended_vlan_discard_pkts), ++ MIB_DESC(1, "MtuExceedDiscardPkts", mtu_exceed_discard_pkts), ++ MIB_DESC(2, "RxGoodBytes", rx_good_bytes), ++ MIB_DESC(2, "RxBadBytes", rx_bad_bytes), ++ MIB_DESC(1, "RxGoodPausePkts", rx_good_pause_pkts), ++ MIB_DESC(1, "RxAlignErrorPkts", rx_align_error_pkts), ++}; ++ + #define MXL862XX_SDMA_PCTRLP(p) (0xbc0 + ((p) * 0x6)) + #define MXL862XX_SDMA_PCTRL_EN BIT(0) + +@@ -1940,6 +1998,110 @@ static int mxl862xx_port_bridge_flags(st + return 0; + } + ++static void mxl862xx_get_strings(struct dsa_switch *ds, int port, ++ u32 stringset, u8 *data) ++{ ++ int i; ++ ++ if (stringset != ETH_SS_STATS) ++ return; ++ ++ for (i = 0; i < ARRAY_SIZE(mxl862xx_mib); i++) ++ ethtool_puts(&data, mxl862xx_mib[i].name); ++} ++ ++static int mxl862xx_get_sset_count(struct dsa_switch *ds, int port, int sset) ++{ ++ if (sset != ETH_SS_STATS) ++ return 0; ++ ++ return ARRAY_SIZE(mxl862xx_mib); ++} ++ ++static int mxl862xx_read_rmon(struct dsa_switch *ds, int port, ++ struct mxl862xx_rmon_port_cnt *cnt) ++{ ++ memset(cnt, 0, sizeof(*cnt)); ++ cnt->port_type = MXL862XX_CTP_PORT; ++ cnt->port_id = cpu_to_le16(port); ++ ++ return MXL862XX_API_READ(ds->priv, MXL862XX_RMON_PORT_GET, *cnt); ++} ++ ++static void mxl862xx_get_ethtool_stats(struct dsa_switch *ds, int port, ++ u64 *data) ++{ ++ const struct mxl862xx_mib_desc *mib; ++ struct mxl862xx_rmon_port_cnt cnt; ++ int ret, i; ++ void *field; ++ ++ ret = mxl862xx_read_rmon(ds, port, &cnt); ++ if (ret) { ++ dev_err(ds->dev, "failed to read RMON stats on port %d\n", port); ++ return; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(mxl862xx_mib); i++) { ++ mib = &mxl862xx_mib[i]; ++ field = (u8 *)&cnt + mib->offset; ++ ++ if (mib->size == 1) ++ *data++ = le32_to_cpu(*(__le32 *)field); ++ else ++ *data++ = le64_to_cpu(*(__le64 *)field); ++ } ++} ++ ++static void mxl862xx_get_eth_mac_stats(struct dsa_switch *ds, int port, ++ struct ethtool_eth_mac_stats *mac_stats) ++{ ++ struct mxl862xx_rmon_port_cnt cnt; ++ ++ if (mxl862xx_read_rmon(ds, port, &cnt)) ++ return; ++ ++ mac_stats->FramesTransmittedOK = le32_to_cpu(cnt.tx_good_pkts); ++ mac_stats->SingleCollisionFrames = le32_to_cpu(cnt.tx_single_coll_count); ++ mac_stats->MultipleCollisionFrames = le32_to_cpu(cnt.tx_mult_coll_count); ++ mac_stats->FramesReceivedOK = le32_to_cpu(cnt.rx_good_pkts); ++ mac_stats->FrameCheckSequenceErrors = le32_to_cpu(cnt.rx_fcserror_pkts); ++ mac_stats->AlignmentErrors = le32_to_cpu(cnt.rx_align_error_pkts); ++ mac_stats->OctetsTransmittedOK = le64_to_cpu(cnt.tx_good_bytes); ++ mac_stats->LateCollisions = le32_to_cpu(cnt.tx_late_coll_count); ++ mac_stats->FramesAbortedDueToXSColls = le32_to_cpu(cnt.tx_excess_coll_count); ++ mac_stats->OctetsReceivedOK = le64_to_cpu(cnt.rx_good_bytes); ++ mac_stats->MulticastFramesXmittedOK = le32_to_cpu(cnt.tx_multicast_pkts); ++ mac_stats->BroadcastFramesXmittedOK = le32_to_cpu(cnt.tx_broadcast_pkts); ++ mac_stats->MulticastFramesReceivedOK = le32_to_cpu(cnt.rx_multicast_pkts); ++ mac_stats->BroadcastFramesReceivedOK = le32_to_cpu(cnt.rx_broadcast_pkts); ++ mac_stats->FrameTooLongErrors = le32_to_cpu(cnt.rx_oversize_error_pkts); ++} ++ ++static void mxl862xx_get_eth_ctrl_stats(struct dsa_switch *ds, int port, ++ struct ethtool_eth_ctrl_stats *ctrl_stats) ++{ ++ struct mxl862xx_rmon_port_cnt cnt; ++ ++ if (mxl862xx_read_rmon(ds, port, &cnt)) ++ return; ++ ++ ctrl_stats->MACControlFramesTransmitted = le32_to_cpu(cnt.tx_pause_count); ++ ctrl_stats->MACControlFramesReceived = le32_to_cpu(cnt.rx_good_pause_pkts); ++} ++ ++static void mxl862xx_get_pause_stats(struct dsa_switch *ds, int port, ++ struct ethtool_pause_stats *pause_stats) ++{ ++ struct mxl862xx_rmon_port_cnt cnt; ++ ++ if (mxl862xx_read_rmon(ds, port, &cnt)) ++ return; ++ ++ pause_stats->tx_pause_frames = le32_to_cpu(cnt.tx_pause_count); ++ pause_stats->rx_pause_frames = le32_to_cpu(cnt.rx_good_pause_pkts); ++} ++ + static const struct dsa_switch_ops mxl862xx_switch_ops = { + .get_tag_protocol = mxl862xx_get_tag_protocol, + .setup = mxl862xx_setup, +@@ -1964,6 +2126,12 @@ static const struct dsa_switch_ops mxl86 + .port_vlan_filtering = mxl862xx_port_vlan_filtering, + .port_vlan_add = mxl862xx_port_vlan_add, + .port_vlan_del = mxl862xx_port_vlan_del, ++ .get_strings = mxl862xx_get_strings, ++ .get_sset_count = mxl862xx_get_sset_count, ++ .get_ethtool_stats = mxl862xx_get_ethtool_stats, ++ .get_eth_mac_stats = mxl862xx_get_eth_mac_stats, ++ .get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats, ++ .get_pause_stats = mxl862xx_get_pause_stats, + }; + + static void mxl862xx_phylink_mac_config(struct phylink_config *config, diff --git a/target/linux/generic/pending-6.12/760-07-net-dsa-mxl862xx-implement-.get_stats64.patch b/target/linux/generic/pending-6.12/760-07-net-dsa-mxl862xx-implement-.get_stats64.patch new file mode 100644 index 00000000000..9d4d93bc2d2 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-07-net-dsa-mxl862xx-implement-.get_stats64.patch @@ -0,0 +1,325 @@ +From 8b66d20f7e5226f4854a39cfb9f25a0591a5bb83 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 04:14:38 +0000 +Subject: [PATCH 16/35] net: dsa: mxl862xx: implement .get_stats64 + +Poll free-running firmware RMON counters every 2 seconds and accumulate +deltas into 64-bit per-port statistics. 32-bit packet counters wrap +in ~880s at 2.5 Gbps line rate; the 2s polling interval provides a +comfortable margin. The .get_stats64 callback forces a fresh poll so +that counters are always up to date when queried. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx.c | 167 ++++++++++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx.h | 51 +++++++++ + 2 files changed, 218 insertions(+) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -30,6 +30,12 @@ + #define MXL862XX_API_READ_QUIET(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) + ++/* Polling interval for RMON counter accumulation. At 2.5 Gbps with ++ * minimum-size (64-byte) frames, a 32-bit packet counter wraps in ~880s. ++ * 2s gives a comfortable margin. ++ */ ++#define MXL862XX_STATS_POLL_INTERVAL (2 * HZ) ++ + struct mxl862xx_mib_desc { + unsigned int size; + unsigned int offset; +@@ -784,6 +790,9 @@ static int mxl862xx_setup(struct dsa_swi + if (ret) + return ret; + ++ schedule_delayed_work(&priv->stats_work, ++ MXL862XX_STATS_POLL_INTERVAL); ++ + return mxl862xx_setup_mdio(ds); + } + +@@ -2102,6 +2111,156 @@ static void mxl862xx_get_pause_stats(str + pause_stats->rx_pause_frames = le32_to_cpu(cnt.rx_good_pause_pkts); + } + ++/* Compute the delta between two 32-bit free-running counter snapshots, ++ * handling a single wrap-around correctly via unsigned subtraction. ++ */ ++static u64 mxl862xx_delta32(u32 cur, u32 prev) ++{ ++ return (u32)(cur - prev); ++} ++ ++/** ++ * mxl862xx_stats_poll - Read RMON counters and accumulate into 64-bit stats ++ * @ds: DSA switch ++ * @port: port index ++ * ++ * The firmware RMON counters are free-running 32-bit values (64-bit for ++ * byte counters). This function reads the hardware via MDIO (may sleep), ++ * computes deltas from the previous snapshot, and accumulates them into ++ * 64-bit per-port stats under a spinlock. ++ * ++ * Called only from the stats polling workqueue -- serialized by the ++ * single-threaded delayed_work, so no MDIO locking is needed here. ++ */ ++static void mxl862xx_stats_poll(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port_stats *s = &priv->ports[port].stats; ++ u32 rx_fcserr, rx_under, rx_over, rx_align, tx_drop; ++ u32 rx_drop, rx_evlan, mtu_exc, tx_acm; ++ struct mxl862xx_rmon_port_cnt cnt; ++ u64 rx_bytes, tx_bytes; ++ u32 rx_mcast, tx_coll; ++ u32 rx_pkts, tx_pkts; ++ ++ /* MDIO read -- may sleep, done outside the spinlock. */ ++ if (mxl862xx_read_rmon(ds, port, &cnt)) ++ return; ++ ++ rx_pkts = le32_to_cpu(cnt.rx_good_pkts); ++ tx_pkts = le32_to_cpu(cnt.tx_good_pkts); ++ rx_bytes = le64_to_cpu(cnt.rx_good_bytes); ++ tx_bytes = le64_to_cpu(cnt.tx_good_bytes); ++ rx_fcserr = le32_to_cpu(cnt.rx_fcserror_pkts); ++ rx_under = le32_to_cpu(cnt.rx_under_size_error_pkts); ++ rx_over = le32_to_cpu(cnt.rx_oversize_error_pkts); ++ rx_align = le32_to_cpu(cnt.rx_align_error_pkts); ++ tx_drop = le32_to_cpu(cnt.tx_dropped_pkts); ++ rx_drop = le32_to_cpu(cnt.rx_dropped_pkts); ++ rx_evlan = le32_to_cpu(cnt.rx_extended_vlan_discard_pkts); ++ mtu_exc = le32_to_cpu(cnt.mtu_exceed_discard_pkts); ++ tx_acm = le32_to_cpu(cnt.tx_acm_dropped_pkts); ++ rx_mcast = le32_to_cpu(cnt.rx_multicast_pkts); ++ tx_coll = le32_to_cpu(cnt.tx_coll_count); ++ ++ /* Accumulate deltas under spinlock -- .get_stats64 reads these. */ ++ spin_lock_bh(&priv->ports[port].stats_lock); ++ ++ s->rx_packets += mxl862xx_delta32(rx_pkts, s->prev_rx_good_pkts); ++ s->tx_packets += mxl862xx_delta32(tx_pkts, s->prev_tx_good_pkts); ++ s->rx_bytes += rx_bytes - s->prev_rx_good_bytes; ++ s->tx_bytes += tx_bytes - s->prev_tx_good_bytes; ++ ++ s->rx_errors += ++ mxl862xx_delta32(rx_fcserr, s->prev_rx_fcserror_pkts) + ++ mxl862xx_delta32(rx_under, s->prev_rx_under_size_error_pkts) + ++ mxl862xx_delta32(rx_over, s->prev_rx_oversize_error_pkts) + ++ mxl862xx_delta32(rx_align, s->prev_rx_align_error_pkts); ++ s->tx_errors += ++ mxl862xx_delta32(tx_drop, s->prev_tx_dropped_pkts); ++ ++ s->rx_dropped += ++ mxl862xx_delta32(rx_drop, s->prev_rx_dropped_pkts) + ++ mxl862xx_delta32(rx_evlan, s->prev_rx_evlan_discard_pkts) + ++ mxl862xx_delta32(mtu_exc, s->prev_mtu_exceed_discard_pkts); ++ s->tx_dropped += ++ mxl862xx_delta32(tx_drop, s->prev_tx_dropped_pkts) + ++ mxl862xx_delta32(tx_acm, s->prev_tx_acm_dropped_pkts); ++ ++ s->multicast += mxl862xx_delta32(rx_mcast, s->prev_rx_multicast_pkts); ++ s->collisions += mxl862xx_delta32(tx_coll, s->prev_tx_coll_count); ++ ++ s->rx_length_errors += ++ mxl862xx_delta32(rx_under, s->prev_rx_under_size_error_pkts) + ++ mxl862xx_delta32(rx_over, s->prev_rx_oversize_error_pkts); ++ s->rx_crc_errors += ++ mxl862xx_delta32(rx_fcserr, s->prev_rx_fcserror_pkts); ++ s->rx_frame_errors += ++ mxl862xx_delta32(rx_align, s->prev_rx_align_error_pkts); ++ ++ s->prev_rx_good_pkts = rx_pkts; ++ s->prev_tx_good_pkts = tx_pkts; ++ s->prev_rx_good_bytes = rx_bytes; ++ s->prev_tx_good_bytes = tx_bytes; ++ s->prev_rx_fcserror_pkts = rx_fcserr; ++ s->prev_rx_under_size_error_pkts = rx_under; ++ s->prev_rx_oversize_error_pkts = rx_over; ++ s->prev_rx_align_error_pkts = rx_align; ++ s->prev_tx_dropped_pkts = tx_drop; ++ s->prev_rx_dropped_pkts = rx_drop; ++ s->prev_rx_evlan_discard_pkts = rx_evlan; ++ s->prev_mtu_exceed_discard_pkts = mtu_exc; ++ s->prev_tx_acm_dropped_pkts = tx_acm; ++ s->prev_rx_multicast_pkts = rx_mcast; ++ s->prev_tx_coll_count = tx_coll; ++ ++ spin_unlock_bh(&priv->ports[port].stats_lock); ++} ++ ++static void mxl862xx_stats_work_fn(struct work_struct *work) ++{ ++ struct mxl862xx_priv *priv = ++ container_of(work, struct mxl862xx_priv, stats_work.work); ++ struct dsa_switch *ds = priv->ds; ++ struct dsa_port *dp; ++ ++ dsa_switch_for_each_available_port(dp, ds) ++ mxl862xx_stats_poll(ds, dp->index); ++ ++ schedule_delayed_work(&priv->stats_work, ++ MXL862XX_STATS_POLL_INTERVAL); ++} ++ ++static void mxl862xx_get_stats64(struct dsa_switch *ds, int port, ++ struct rtnl_link_stats64 *s) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port_stats *ps = &priv->ports[port].stats; ++ ++ spin_lock_bh(&priv->ports[port].stats_lock); ++ ++ s->rx_packets = ps->rx_packets; ++ s->tx_packets = ps->tx_packets; ++ s->rx_bytes = ps->rx_bytes; ++ s->tx_bytes = ps->tx_bytes; ++ s->rx_errors = ps->rx_errors; ++ s->tx_errors = ps->tx_errors; ++ s->rx_dropped = ps->rx_dropped; ++ s->tx_dropped = ps->tx_dropped; ++ s->multicast = ps->multicast; ++ s->collisions = ps->collisions; ++ s->rx_length_errors = ps->rx_length_errors; ++ s->rx_crc_errors = ps->rx_crc_errors; ++ s->rx_frame_errors = ps->rx_frame_errors; ++ ++ spin_unlock_bh(&priv->ports[port].stats_lock); ++ ++ /* Trigger a fresh poll so the next read sees up-to-date counters. ++ * No-op if the work is already pending or running. ++ */ ++ schedule_delayed_work(&priv->stats_work, 0); ++} ++ + static const struct dsa_switch_ops mxl862xx_switch_ops = { + .get_tag_protocol = mxl862xx_get_tag_protocol, + .setup = mxl862xx_setup, +@@ -2132,6 +2291,7 @@ static const struct dsa_switch_ops mxl86 + .get_eth_mac_stats = mxl862xx_get_eth_mac_stats, + .get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats, + .get_pause_stats = mxl862xx_get_pause_stats, ++ .get_stats64 = mxl862xx_get_stats64, + }; + + static void mxl862xx_phylink_mac_config(struct phylink_config *config, +@@ -2193,8 +2353,11 @@ static int mxl862xx_probe(struct mdio_de + priv->ports[i].priv = priv; + INIT_WORK(&priv->ports[i].host_flood_work, + mxl862xx_host_flood_work_fn); ++ spin_lock_init(&priv->ports[i].stats_lock); + } + ++ INIT_DELAYED_WORK(&priv->stats_work, mxl862xx_stats_work_fn); ++ + dev_set_drvdata(dev, ds); + + return dsa_register_switch(ds); +@@ -2213,6 +2376,8 @@ static void mxl862xx_remove(struct mdio_ + + dsa_unregister_switch(ds); + ++ cancel_delayed_work_sync(&priv->stats_work); ++ + mxl862xx_host_shutdown(priv); + + /* Cancel any pending host flood work. dsa_unregister_switch() +@@ -2237,6 +2402,8 @@ static void mxl862xx_shutdown(struct mdi + + dsa_switch_shutdown(ds); + ++ cancel_delayed_work_sync(&priv->stats_work); ++ + mxl862xx_host_shutdown(priv); + + for (i = 0; i < MXL862XX_MAX_PORTS; i++) +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -148,6 +148,47 @@ struct mxl862xx_evlan_block { + }; + + /** ++ * struct mxl862xx_port_stats - 64-bit accumulated hardware port statistics ++ * ++ * The firmware RMON counters are 32-bit free-running (64-bit for byte ++ * counters). This structure holds 64-bit accumulators alongside the ++ * previous raw snapshot so that deltas can be computed across polls, ++ * handling 32-bit wrap correctly via unsigned subtraction. ++ */ ++struct mxl862xx_port_stats { ++ /* 64-bit accumulators */ ++ u64 rx_packets; ++ u64 tx_packets; ++ u64 rx_bytes; ++ u64 tx_bytes; ++ u64 rx_errors; ++ u64 tx_errors; ++ u64 rx_dropped; ++ u64 tx_dropped; ++ u64 multicast; ++ u64 collisions; ++ u64 rx_length_errors; ++ u64 rx_crc_errors; ++ u64 rx_frame_errors; ++ /* Previous raw RMON values for delta computation */ ++ u32 prev_rx_good_pkts; ++ u32 prev_tx_good_pkts; ++ u64 prev_rx_good_bytes; ++ u64 prev_tx_good_bytes; ++ u32 prev_rx_fcserror_pkts; ++ u32 prev_rx_under_size_error_pkts; ++ u32 prev_rx_oversize_error_pkts; ++ u32 prev_rx_align_error_pkts; ++ u32 prev_tx_dropped_pkts; ++ u32 prev_rx_dropped_pkts; ++ u32 prev_rx_evlan_discard_pkts; ++ u32 prev_mtu_exceed_discard_pkts; ++ u32 prev_tx_acm_dropped_pkts; ++ u32 prev_rx_multicast_pkts; ++ u32 prev_tx_coll_count; ++}; ++ ++/** + * struct mxl862xx_port - per-port state tracked by the driver + * @priv: back-pointer to switch private data; needed by + * deferred work handlers to access ds and priv +@@ -178,6 +219,10 @@ struct mxl862xx_evlan_block { + * The worker acquires rtnl_lock() to serialize with + * DSA callbacks and checks @setup_done to avoid + * acting on torn-down ports. ++ * @stats: 64-bit accumulated hardware statistics; updated ++ * periodically by the stats polling work ++ * @stats_lock: protects accumulator reads in .get_stats64 against ++ * concurrent updates from the polling work + */ + struct mxl862xx_port { + struct mxl862xx_priv *priv; +@@ -195,6 +240,9 @@ struct mxl862xx_port { + bool host_flood_uc; + bool host_flood_mc; + struct work_struct host_flood_work; ++ /* Hardware stats accumulation */ ++ struct mxl862xx_port_stats stats; ++ spinlock_t stats_lock; + }; + + /** +@@ -216,6 +264,8 @@ struct mxl862xx_port { + * @evlan_ingress_size: per-port ingress Extended VLAN block size + * @evlan_egress_size: per-port egress Extended VLAN block size + * @vf_block_size: per-port VLAN Filter block size ++ * @stats_work: periodic work item that polls RMON hardware counters ++ * and accumulates them into 64-bit per-port stats + */ + struct mxl862xx_priv { + struct dsa_switch *ds; +@@ -228,6 +278,7 @@ struct mxl862xx_priv { + u16 evlan_ingress_size; + u16 evlan_egress_size; + u16 vf_block_size; ++ struct delayed_work stats_work; + }; + + #endif /* __MXL862XX_H */ diff --git a/target/linux/generic/pending-6.12/760-08-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch b/target/linux/generic/pending-6.12/760-08-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch new file mode 100644 index 00000000000..d62b466029b --- /dev/null +++ b/target/linux/generic/pending-6.12/760-08-net-dsa-mxl862xx-store-firmware-version-for-feature-.patch @@ -0,0 +1,98 @@ +From fecfbea928cd762b19ff17aa16fb1ab143d73db1 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 17:56:35 +0000 +Subject: [PATCH 17/35] net: dsa: mxl862xx: store firmware version for feature + gating + +Query the firmware version at init (already done in wait_ready), +cache it in priv->fw_version, and provide MXL862XX_FW_VER_MIN() +for version-gated code paths throughout the driver. + +The union mxl862xx_fw_version lays out major/minor/revision so +that the u32 raw field compares with natural version ordering on +both big- and little-endian machines. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx.c | 3 +++ + drivers/net/dsa/mxl862xx/mxl862xx.h | 36 +++++++++++++++++++++++++++++ + 2 files changed, 39 insertions(+) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -286,6 +286,9 @@ static int mxl862xx_wait_ready(struct ds + ver.iv_major, ver.iv_minor, + le16_to_cpu(ver.iv_revision), + le32_to_cpu(ver.iv_build_num)); ++ priv->fw_version.major = ver.iv_major; ++ priv->fw_version.minor = ver.iv_minor; ++ priv->fw_version.revision = le16_to_cpu(ver.iv_revision); + return 0; + + not_ready_yet: +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -3,6 +3,7 @@ + #ifndef __MXL862XX_H + #define __MXL862XX_H + ++#include + #include + #include + #include +@@ -246,6 +247,38 @@ struct mxl862xx_port { + }; + + /** ++ * union mxl862xx_fw_version - firmware version for comparison and display ++ * @major: firmware major version ++ * @minor: firmware minor version ++ * @revision: firmware revision number ++ * @raw: combined u32 for direct >= comparison (major most significant) ++ * ++ * The struct layout places major in the most-significant byte of the ++ * u32 on both big- and little-endian machines, so raw values compare ++ * with the natural major > minor > revision ordering. ++ */ ++union mxl862xx_fw_version { ++ struct { ++#if defined(__BIG_ENDIAN) ++ u8 major; ++ u8 minor; ++ u16 revision; ++#elif defined(__LITTLE_ENDIAN) ++ u16 revision; ++ u8 minor; ++ u8 major; ++#endif ++ }; ++ u32 raw; ++}; ++ ++#define MXL862XX_FW_VER(maj, min, rev) \ ++ ((union mxl862xx_fw_version){ .major = (maj), .minor = (min), \ ++ .revision = (rev) }).raw ++#define MXL862XX_FW_VER_MIN(priv, maj, min, rev) \ ++ ((priv)->fw_version.raw >= MXL862XX_FW_VER(maj, min, rev)) ++ ++/** + * struct mxl862xx_priv - driver private data for an MxL862xx switch + * @ds: pointer to the DSA switch instance + * @mdiodev: MDIO device used to communicate with the switch firmware +@@ -256,6 +289,8 @@ struct mxl862xx_port { + * @drop_meter: index of the single shared zero-rate firmware meter + * used to unconditionally drop traffic (used to block + * flooding) ++ * @fw_version: cached firmware version, populated at probe and ++ * compared with MXL862XX_FW_VER_MIN() + * @ports: per-port state, indexed by switch port number + * @bridges: maps DSA bridge number to firmware bridge ID; + * zero means no firmware bridge allocated for that +@@ -273,6 +308,7 @@ struct mxl862xx_priv { + struct work_struct crc_err_work; + unsigned long crc_err; + u16 drop_meter; ++ union mxl862xx_fw_version fw_version; + struct mxl862xx_port ports[MXL862XX_MAX_PORTS]; + u16 bridges[MXL862XX_MAX_BRIDGES + 1]; + u16 evlan_ingress_size; diff --git a/target/linux/generic/pending-6.12/760-09-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch b/target/linux/generic/pending-6.12/760-09-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch new file mode 100644 index 00000000000..833ec0642ed --- /dev/null +++ b/target/linux/generic/pending-6.12/760-09-net-dsa-mxl862xx-move-phylink-stubs-to-mxl862xx-phyl.patch @@ -0,0 +1,163 @@ +From 3cb224514226928df80e43ca2280c7dca654bdfe Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 25 Mar 2026 21:39:30 +0000 +Subject: [PATCH 18/35] net: dsa: mxl862xx: move phylink stubs to + mxl862xx-phylink.c + +Move the phylink MAC operations and get_caps callback from mxl862xx.c +into a dedicated mxl862xx-phylink.c file. This prepares for the SerDes +PCS implementation which adds substantial phylink/PCS code -- keeping +it in a separate file avoids function-position churn in the main +driver file. + +No functional change. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/Makefile | 2 +- + drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 51 +++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 14 ++++++ + drivers/net/dsa/mxl862xx/mxl862xx.c | 38 +-------------- + 4 files changed, 67 insertions(+), 38 deletions(-) + create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-phylink.c + create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-phylink.h + +--- a/drivers/net/dsa/mxl862xx/Makefile ++++ b/drivers/net/dsa/mxl862xx/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o +-mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o ++mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o mxl862xx-phylink.o +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c +@@ -0,0 +1,51 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Phylink and PCS support for MaxLinear MxL862xx switch family ++ * ++ * Copyright (C) 2024 MaxLinear Inc. ++ * Copyright (C) 2025 John Crispin ++ * Copyright (C) 2025 Daniel Golle ++ */ ++ ++#include ++#include ++ ++#include "mxl862xx.h" ++#include "mxl862xx-phylink.h" ++ ++void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) ++{ ++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | ++ MAC_100 | MAC_1000 | MAC_2500FD; ++ ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); ++} ++ ++static void mxl862xx_phylink_mac_config(struct phylink_config *config, ++ unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++} ++ ++static void mxl862xx_phylink_mac_link_down(struct phylink_config *config, ++ unsigned int mode, ++ phy_interface_t interface) ++{ ++} ++ ++static void mxl862xx_phylink_mac_link_up(struct phylink_config *config, ++ struct phy_device *phydev, ++ unsigned int mode, ++ phy_interface_t interface, ++ int speed, int duplex, ++ bool tx_pause, bool rx_pause) ++{ ++} ++ ++const struct phylink_mac_ops mxl862xx_phylink_mac_ops = { ++ .mac_config = mxl862xx_phylink_mac_config, ++ .mac_link_down = mxl862xx_phylink_mac_link_down, ++ .mac_link_up = mxl862xx_phylink_mac_link_up, ++}; +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h +@@ -0,0 +1,14 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++ ++#ifndef __MXL862XX_PHYLINK_H ++#define __MXL862XX_PHYLINK_H ++ ++#include ++ ++#include "mxl862xx.h" ++ ++extern const struct phylink_mac_ops mxl862xx_phylink_mac_ops; ++void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config); ++ ++#endif /* __MXL862XX_PHYLINK_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -22,6 +22,7 @@ + #include "mxl862xx-api.h" + #include "mxl862xx-cmd.h" + #include "mxl862xx-host.h" ++#include "mxl862xx-phylink.h" + + #define MXL862XX_API_WRITE(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) +@@ -1642,16 +1643,6 @@ static void mxl862xx_port_teardown(struc + priv->ports[port].setup_done = false; + } + +-static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, +- struct phylink_config *config) +-{ +- config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | +- MAC_100 | MAC_1000 | MAC_2500FD; +- +- __set_bit(PHY_INTERFACE_MODE_INTERNAL, +- config->supported_interfaces); +-} +- + static int mxl862xx_get_fid(struct dsa_switch *ds, struct dsa_db db) + { + struct mxl862xx_priv *priv = ds->priv; +@@ -2297,33 +2288,6 @@ static const struct dsa_switch_ops mxl86 + .get_stats64 = mxl862xx_get_stats64, + }; + +-static void mxl862xx_phylink_mac_config(struct phylink_config *config, +- unsigned int mode, +- const struct phylink_link_state *state) +-{ +-} +- +-static void mxl862xx_phylink_mac_link_down(struct phylink_config *config, +- unsigned int mode, +- phy_interface_t interface) +-{ +-} +- +-static void mxl862xx_phylink_mac_link_up(struct phylink_config *config, +- struct phy_device *phydev, +- unsigned int mode, +- phy_interface_t interface, +- int speed, int duplex, +- bool tx_pause, bool rx_pause) +-{ +-} +- +-static const struct phylink_mac_ops mxl862xx_phylink_mac_ops = { +- .mac_config = mxl862xx_phylink_mac_config, +- .mac_link_down = mxl862xx_phylink_mac_link_down, +- .mac_link_up = mxl862xx_phylink_mac_link_up, +-}; +- + static int mxl862xx_probe(struct mdio_device *mdiodev) + { + struct device *dev = &mdiodev->dev; diff --git a/target/linux/generic/pending-6.12/760-10-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch b/target/linux/generic/pending-6.12/760-10-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch new file mode 100644 index 00000000000..01013eec3e7 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-10-net-dsa-mxl862xx-move-API-macros-to-mxl862xx-host.h.patch @@ -0,0 +1,53 @@ +From de41d438c4e90876449715a307dd03fa37338742 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 26 Mar 2026 01:50:00 +0000 +Subject: [PATCH 19/35] net: dsa: mxl862xx: move API macros to mxl862xx-host.h + +Move the MXL862XX_API_WRITE, MXL862XX_API_READ and +MXL862XX_API_READ_QUIET convenience macros from mxl862xx.c to +mxl862xx-host.h next to the mxl862xx_api_wrap() prototype they wrap. +This makes them available to other compilation units that include +mxl862xx-host.h, which is needed once the SerDes PCS code in +mxl862xx-phylink.c also calls firmware commands. + +No functional change. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-host.h | 8 ++++++++ + drivers/net/dsa/mxl862xx/mxl862xx.c | 7 ------- + 2 files changed, 8 insertions(+), 7 deletions(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.h +@@ -9,6 +9,14 @@ void mxl862xx_host_init(struct mxl862xx_ + void mxl862xx_host_shutdown(struct mxl862xx_priv *priv); + int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *data, u16 size, + bool read, bool quiet); ++ ++#define MXL862XX_API_WRITE(dev, cmd, data) \ ++ mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) ++#define MXL862XX_API_READ(dev, cmd, data) \ ++ mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, false) ++#define MXL862XX_API_READ_QUIET(dev, cmd, data) \ ++ mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) ++ + int mxl862xx_reset(struct mxl862xx_priv *priv); + + #endif /* __MXL862XX_HOST_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -24,13 +24,6 @@ + #include "mxl862xx-host.h" + #include "mxl862xx-phylink.h" + +-#define MXL862XX_API_WRITE(dev, cmd, data) \ +- mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) +-#define MXL862XX_API_READ(dev, cmd, data) \ +- mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, false) +-#define MXL862XX_API_READ_QUIET(dev, cmd, data) \ +- mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) +- + /* Polling interval for RMON counter accumulation. At 2.5 Gbps with + * minimum-size (64-byte) frames, a 32-bit packet counter wraps in ~880s. + * 2s gives a comfortable margin. diff --git a/target/linux/generic/pending-6.12/760-11-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch b/target/linux/generic/pending-6.12/760-11-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch new file mode 100644 index 00000000000..790f50c87df --- /dev/null +++ b/target/linux/generic/pending-6.12/760-11-net-dsa-mxl862xx-add-support-for-SerDes-ports.patch @@ -0,0 +1,1053 @@ +From 88f46eb32d1aed296af2005c3ed8f23a6eea64c3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 22 Mar 2026 00:57:44 +0000 +Subject: [PATCH 20/35] net: dsa: mxl862xx: add support for SerDes ports + +The MxL862xx has two XPCS/SerDes interfaces (XPCS0 for ports 9-12, +XPCS1 for ports 13-16). Each can operate in various single-lane +modes (SGMII, 1000BASE-X, 2500BASE-X, 10GBASE-R, 10GBASE-KR, +USXGMII) or as QSGMII providing four sub-ports. + +Implement phylink PCS operations using the firmware's XPCS API: + + - pcs_pre_config: power-sequence the SerDes (hard reset if already + running, then PCS_ENABLE with the target interface mode), polling + SIGNAL_DETECT until the XPCS exits reset. + - pcs_config: configure negotiation mode and CL37/SGMII advertising. + - pcs_get_state: read link/speed/duplex/LPA from firmware and decode + using phylink's standard CL37, SGMII, and USXGMII decoders, with + firmware-resolved speed/duplex override for downshift detection. + - pcs_an_restart: restart CL37 or CL73 auto-negotiation. + - pcs_link_up: force speed/duplex for SGMII. + - pcs_inband_caps: report per-mode in-band status capabilities. + +Register a PCS instance for each SerDes port and QSGMII sub-port +during setup. Advertise the supported interface modes in +phylink_get_caps based on port number. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 474 +++++++++++++++++++- + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 13 + + drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 411 ++++++++++++++++- + drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 2 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 5 +- + drivers/net/dsa/mxl862xx/mxl862xx.h | 19 + + 6 files changed, 907 insertions(+), 17 deletions(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -1185,6 +1185,242 @@ struct mxl862xx_ctp_port_assignment { + } __packed; + + /** ++ * enum mxl862xx_port_duplex - Ethernet port duplex status ++ * @MXL862XX_DUPLEX_FULL: Port operates in full-duplex mode ++ * @MXL862XX_DUPLEX_HALF: Port operates in half-duplex mode ++ * @MXL862XX_DUPLEX_AUTO: Port operates in Auto mode ++ */ ++enum mxl862xx_port_duplex { ++ MXL862XX_DUPLEX_FULL = 0, ++ MXL862XX_DUPLEX_HALF, ++ MXL862XX_DUPLEX_AUTO, ++}; ++ ++/** ++ * enum mxl862xx_port_type - Port Type ++ * @MXL862XX_LOGICAL_PORT: Logical Port ++ * @MXL862XX_PHYSICAL_PORT: Physical Port ++ * @MXL862XX_CTP_PORT: Connectivity Termination Port (CTP) ++ * @MXL862XX_BRIDGE_PORT: Bridge Port ++ */ ++enum mxl862xx_port_type { ++ MXL862XX_LOGICAL_PORT = 0, ++ MXL862XX_PHYSICAL_PORT, ++ MXL862XX_CTP_PORT, ++ MXL862XX_BRIDGE_PORT, ++}; ++ ++/** ++ * enum mxl862xx_port_enable - port enable type selection. ++ * @MXL862XX_PORT_DISABLE: the port is disabled in both directions ++ * @MXL862XX_PORT_ENABLE_RXTX: the port is enabled in both directions ++ * @MXL862XX_PORT_ENABLE_RX: the port is enabled in the receive direction ++ * @MXL862XX_PORT_ENABLE_TX: the port is enabled in the transmit direction ++ */ ++enum mxl862xx_port_enable{ ++ MXL862XX_PORT_DISABLE = 0, ++ MXL862XX_PORT_ENABLE_RXTX, ++ MXL862XX_PORT_ENABLE_RX, ++ MXL862XX_PORT_ENABLE_TX, ++}; ++ ++/** ++ * enum mxl862xx_port_flow - ethernet flow control status ++ * @MXL862XX_FLOW_AUTO: automatic flow control ++ * @MXL862XX_FLOW_RX: receive flow control only ++ * @MXL862XX_FLOW_TX: transmit flow control only ++ * @MXL862XX_FLOW_RXTX: receive and transmit flow control ++ * @MXL862XX_FLOW_OFF: no flow control ++ */ ++enum mxl862xx_port_flow { ++ MXL862XX_FLOW_AUTO = 0, ++ MXL862XX_FLOW_RX, ++ MXL862XX_FLOW_TX, ++ MXL862XX_FLOW_RXTX, ++ MXL862XX_FLOW_OFF, ++}; ++ ++/** ++ * enum mxl862xx_port_monitor - port mirror options ++ * @MXL862XX_PORT_MONITOR_NONE: mirroring is disabled ++ * @MXL862XX_PORT_MONITOR_RX: ingress packets are mirrored ++ * @MXL862XX_PORT_MONITOR_TX: egress packets are mirrored ++ * @MXL862XX_PORT_MONITOR_RXTX: ingress and egress packets are mirrored ++ * @MXL862XX_PORT_MONITOR_VLAN_UNKNOWN: mirroring of 'unknown VLAN violation' frames ++ * @MXL862XX_PORT_MONITOR_VLAN_MEMBERSHIP: mirroring of 'VLAN ingress or egress membership ++ violation' frames ++ * @MXL862XX_PORT_MONITOR_PORT_STATE: mirroring of 'port state violation' frames ++ * @MXL862XX_PORT_MONITOR_LEARNING_LIMIT: mirroring of 'MAC learning limit violation' frames ++ * @MXL862XX_PORT_MONITOR_PORT_LOCK: mirroring of 'port lock violation' frames ++ */ ++enum mxl862xx_port_monitor { ++ MXL862XX_PORT_MONITOR_NONE = 0, ++ MXL862XX_PORT_MONITOR_RX, ++ MXL862XX_PORT_MONITOR_TX, ++ MXL862XX_PORT_MONITOR_RXTX, ++ MXL862XX_PORT_MONITOR_VLAN_UNKNOWN, ++ MXL862XX_PORT_MONITOR_VLAN_MEMBERSHIP = 16, ++ MXL862XX_PORT_MONITOR_PORT_STATE = 32, ++ MXL862XX_PORT_MONITOR_LEARNING_LIMIT = 64, ++ MXL862XX_PORT_MONITOR_PORT_LOCK = 128, ++}; ++ ++/** ++ * enum mxl862xx_if_rmon_mode - interface RMON counter mode ++ * @MXL862XX_IF_RMON_FID: FID based RMON counters ++ * @MXL862XX_IF_RMON_SUBID: sub-interface ID based ++ * @MXL862XX_IF_RMON_FLOWID_LSB: flow ID based (bits 3:0) ++ * @MXL862XX_IF_RMON_FLOWID_MSB: flow ID based (bits 7:4) ++ */ ++enum mxl862xx_if_rmon_mode { ++ MXL862XX_IF_RMON_FID = 0, ++ MXL862XX_IF_RMON_SUBID, ++ MXL862XX_IF_RMON_FLOWID_LSB, ++ MXL862XX_IF_RMON_FLOWID_MSB, ++}; ++ ++/** ++ * struct mxl862xx_port_cfg - Port Configuration Parameters ++ * @port_type: See &enum mxl862xx_port_type ++ * @port_id: Ethernet Port number (zero-based counting) ++ * @enable: See &enum mxl862xx_port_enable ++ * @unicast_unknown_drop: Drop unknown unicast packets ++ * @multicast_unknown_drop: Drop unknown multicast packets ++ * @reserved_packet_drop: Drop reserved packet types ++ * @broadcast_drop: Drop broadcast packets ++ * @aging: Enables MAC address table aging. ++ * @learning: MAC address table learning ++ * @learning_mac_port_lock: Automatic MAC address table learning locking on the port ++ * @learning_limit: Automatic MAC address table learning limitation on this port ++ * @mac_spoofing_detection: MAC spoofing detection. Identifies ingress packets that carry ++ * a MAC source address which was previously learned on a different ingress port ++ * @flow_ctrl: See &enum mxl862xx_port_flow ++ * @port_monitor: See &enum mxl862xx_port_monitor ++ * @if_counters: Assign Interface RMON Counters for this Port ++ * @if_count_start_idx: Interface RMON Counters Start Index ++ * @if_rmonmode: See &enum mxl862xx_if_rmon_mode ++ */ ++struct mxl862xx_port_cfg { ++ __le32 port_type; /* enum mxl862xx_port_type */ ++ __le16 port_id; ++ __le32 enable; /* enum mxl862xx_port_enable */ ++ u8 unicast_unknown_drop; ++ u8 multicast_unknown_drop; ++ u8 reserved_packet_drop; ++ u8 broadcast_drop; ++ u8 aging; ++ u8 learning; ++ u8 learning_mac_port_lock; ++ __le16 learning_limit; ++ u8 mac_spoofing_detection; ++ __le32 flow_ctrl; /* enum mxl862xx_port_flow */ ++ __le32 port_monitor; /* enum mxl862xx_port_monitor */ ++ u8 if_counters; ++ __le32 if_count_start_idx; ++ __le32 if_rmonmode; /* enum mxl862xx_if_rmon_mode */ ++} __packed; ++ ++/** ++ * enum mxl862xx_port_speed - Ethernet port speed mode ++ * @MXL862XX_PORT_SPEED_10: 10 Mbit/s ++ * @MXL862XX_PORT_SPEED_100: 100 Mbit/s ++ * @MXL862XX_PORT_SPEED_200: 200 Mbit/s ++ * @MXL862XX_PORT_SPEED_1000: 1000 Mbit/s ++ * @MXL862XX_PORT_SPEED_2500: 2.5 Gbit/s ++ * @MXL862XX_PORT_SPEED_5000: 5 Gbit/s ++ * @MXL862XX_PORT_SPEED_10000: 10 Gbit/s ++ * @MXL862XX_PORT_SPEED_AUTO: Auto speed for XGMAC ++ */ ++enum mxl862xx_port_speed { ++ MXL862XX_PORT_SPEED_10 = 0, ++ MXL862XX_PORT_SPEED_100, ++ MXL862XX_PORT_SPEED_200, ++ MXL862XX_PORT_SPEED_1000, ++ MXL862XX_PORT_SPEED_2500, ++ MXL862XX_PORT_SPEED_5000, ++ MXL862XX_PORT_SPEED_10000, ++ MXL862XX_PORT_SPEED_AUTO, ++}; ++ ++/** ++ * enum mxl862xx_port_link - Force the MAC and PHY link modus ++ * @MXL862XX_PORT_LINK_UP: Link up ++ * @MXL862XX_PORT_LINK_DOWN: Link down ++ * @MXL862XX_PORT_LINK_AUTO: Link Auto ++ */ ++enum mxl862xx_port_link { ++ MXL862XX_PORT_LINK_UP = 0, ++ MXL862XX_PORT_LINK_DOWN, ++ MXL862XX_PORT_LINK_AUTO, ++}; ++ ++/** ++ * enum mxl862xx_mii_mode - Ethernet port interface mode ++ * @MXL862XX_PORT_HW_MII: Normal PHY interface ++ * @MXL862XX_PORT_HW_RMII: Reduced MII interface in normal mode ++ * @MXL862XX_PORT_HW_GMII: GMII or MII, depending upon the speed ++ * @MXL862XX_PORT_HW_RGMII: RGMII mode ++ * @MXL862XX_PORT_HW_XGMII: XGMII mode ++ */ ++enum mxl862xx_mii_mode { ++ MXL862XX_PORT_HW_MII = 0, ++ MXL862XX_PORT_HW_RMII, ++ MXL862XX_PORT_HW_GMII, ++ MXL862XX_PORT_HW_RGMII, ++ MXL862XX_PORT_HW_XGMII, ++}; ++ ++/** ++ * enum mxl862xx_mii_type - Ethernet port configuration for PHY or MAC mode ++ * @MXL862XX_PORT_MAC: The Ethernet port is configured to work in MAC mode ++ * @MXL862XX_PORT_PHY: The Ethernet port is configured to work in PHY mode ++ */ ++enum mxl862xx_mii_type { ++ MXL862XX_PORT_MAC = 0, ++ MXL862XX_PORT_PHY, ++}; ++ ++/** ++ * enum mxl862xx_clk_mode - Ethernet port clock source configuration ++ * @MXL862XX_PORT_CLK_NA: Clock Mode not applicable ++ * @MXL862XX_PORT_CLK_MASTER: Clock Master Mode. The port is configured to provide the clock as output signal ++ * @MXL862XX_PORT_CLK_SLAVE: Clock Slave Mode. The port is configured to use the input clock signal ++ */ ++enum mxl862xx_clk_mode { ++ MXL862XX_PORT_CLK_NA = 0, ++ MXL862XX_PORT_CLK_MASTER, ++ MXL862XX_PORT_CLK_SLAVE, ++}; ++ ++/** ++ * struct mxl862xx_port_link_cfg - Ethernet port link, speed status and flow control status ++ * @port_id: Ethernet Port number ++ * @duplex_force: Force Port Duplex Mode ++ * @duplex: See &enum mxl862xx_port_duplex ++ * @speed_force: Force Link Speed ++ * @speed: See &enum mxl862xx_port_speed ++ * @link_force: Force Link ++ * @link: See &enum mxl862xx_port_link ++ * @mii_mode: See &enum mxl862xx_mii_mode ++ * @mii_type: See &enum mxl862xx_mii_type ++ * @clk_mode: See &enum mxl862xx_clk_mode ++ * @lpi: 'Low Power Idle' Support for 'Energy Efficient Ethernet' ++ */ ++struct mxl862xx_port_link_cfg { ++ __le16 port_id; ++ u8 duplex_force; ++ __le32 duplex; /* enum mxl862xx_port_duplex */ ++ u8 speed_force; ++ __le32 speed; /* enum mxl862xx_port_speed */ ++ u8 link_force; ++ __le32 link; /* enum mxl862xx_port_link */ ++ __le32 mii_mode; /* enum mxl862xx_mii_mode */ ++ __le32 mii_type; /* enum mxl862xx_mii_type */ ++ __le32 clk_mode; /* enum mxl862xx_clk_mode */ ++ u8 lpi; ++} __packed; ++ ++/** + * enum mxl862xx_stp_port_state - Spanning Tree Protocol port states + * @MXL862XX_STP_PORT_STATE_FORWARD: Forwarding state + * @MXL862XX_STP_PORT_STATE_DISABLE: Disabled/Discarding state +@@ -1225,20 +1461,6 @@ struct mxl862xx_sys_fw_image_version { + } __packed; + + /** +- * enum mxl862xx_port_type - Port Type +- * @MXL862XX_LOGICAL_PORT: Logical Port +- * @MXL862XX_PHYSICAL_PORT: Physical Port +- * @MXL862XX_CTP_PORT: Connectivity Termination Port (CTP) +- * @MXL862XX_BRIDGE_PORT: Bridge Port +- */ +-enum mxl862xx_port_type { +- MXL862XX_LOGICAL_PORT = 0, +- MXL862XX_PHYSICAL_PORT, +- MXL862XX_CTP_PORT, +- MXL862XX_BRIDGE_PORT, +-}; +- +-/** + * enum mxl862xx_rmon_port_type - RMON counter table type + * @MXL862XX_RMON_CTP_PORT_RX: CTP RX counters + * @MXL862XX_RMON_CTP_PORT_TX: CTP TX counters +@@ -1366,4 +1588,228 @@ struct mxl862xx_rmon_port_cnt { + __le64 tx_good_bytes; + } __packed; + ++/** ++ * enum mxl862xx_xpcs_if_mode - XPCS interface mode ++ * @MXL862XX_XPCS_IF_SGMII: SGMII ++ * @MXL862XX_XPCS_IF_1000BASEX: 1000BASE-X ++ * @MXL862XX_XPCS_IF_2500BASEX: 2500BASE-X ++ * @MXL862XX_XPCS_IF_USXGMII: USXGMII ++ * @MXL862XX_XPCS_IF_10GBASER: 10GBASE-R ++ * @MXL862XX_XPCS_IF_10GKR: 10GBASE-KR ++ * @MXL862XX_XPCS_IF_5GBASER: 5GBASE-R ++ * @MXL862XX_XPCS_IF_QSGMII: QSGMII ++ */ ++enum mxl862xx_xpcs_if_mode { ++ MXL862XX_XPCS_IF_SGMII = 0, ++ MXL862XX_XPCS_IF_1000BASEX = 1, ++ MXL862XX_XPCS_IF_2500BASEX = 2, ++ MXL862XX_XPCS_IF_USXGMII = 3, ++ MXL862XX_XPCS_IF_10GBASER = 4, ++ MXL862XX_XPCS_IF_10GKR = 5, ++ MXL862XX_XPCS_IF_5GBASER = 6, ++ MXL862XX_XPCS_IF_QSGMII = 7, ++}; ++ ++/** ++ * enum mxl862xx_xpcs_neg_mode - PCS negotiation mode ++ * @MXL862XX_XPCS_NEG_NONE: no inband negotiation ++ * @MXL862XX_XPCS_NEG_INBAND_AN_OFF: inband selected but AN disabled ++ * @MXL862XX_XPCS_NEG_INBAND_AN_ON: inband with AN enabled ++ */ ++enum mxl862xx_xpcs_neg_mode { ++ MXL862XX_XPCS_NEG_NONE = 0, ++ MXL862XX_XPCS_NEG_INBAND_AN_OFF = 1, ++ MXL862XX_XPCS_NEG_INBAND_AN_ON = 2, ++}; ++ ++/** ++ * enum mxl862xx_xpcs_speed - PCS speed values ++ * @MXL862XX_XPCS_SPEED_UNKNOWN: unknown speed ++ * @MXL862XX_XPCS_SPEED_10: 10 Mbps ++ * @MXL862XX_XPCS_SPEED_100: 100 Mbps ++ * @MXL862XX_XPCS_SPEED_1000: 1000 Mbps ++ * @MXL862XX_XPCS_SPEED_2500: 2500 Mbps ++ * @MXL862XX_XPCS_SPEED_5000: 5000 Mbps ++ * @MXL862XX_XPCS_SPEED_10000: 10000 Mbps ++ */ ++enum mxl862xx_xpcs_speed { ++ MXL862XX_XPCS_SPEED_UNKNOWN = 0, ++ MXL862XX_XPCS_SPEED_10 = 10, ++ MXL862XX_XPCS_SPEED_100 = 100, ++ MXL862XX_XPCS_SPEED_1000 = 1000, ++ MXL862XX_XPCS_SPEED_2500 = 2500, ++ MXL862XX_XPCS_SPEED_5000 = 5000, ++ MXL862XX_XPCS_SPEED_10000 = 10000, ++}; ++ ++/** ++ * enum mxl862xx_xpcs_duplex - PCS duplex mode ++ * @MXL862XX_XPCS_DUPLEX_HALF: half duplex ++ * @MXL862XX_XPCS_DUPLEX_FULL: full duplex ++ */ ++enum mxl862xx_xpcs_duplex { ++ MXL862XX_XPCS_DUPLEX_HALF = 0, ++ MXL862XX_XPCS_DUPLEX_FULL = 1, ++}; ++ ++/** ++ * enum mxl862xx_xpcs_loopback_mode - XPCS loopback mode ++ * @MXL862XX_XPCS_LB_DISABLE: disable all loopback ++ * @MXL862XX_XPCS_LB_PCS_SERIAL: PCS TX-to-RX serial loopback ++ * @MXL862XX_XPCS_LB_PCS_PARALLEL: PCS RX-to-TX parallel loopback ++ * @MXL862XX_XPCS_LB_PMA_SERIAL: PMA TX-to-RX serial loopback ++ * @MXL862XX_XPCS_LB_PMA_PARALLEL: PMA RX-to-TX parallel loopback ++ */ ++enum mxl862xx_xpcs_loopback_mode { ++ MXL862XX_XPCS_LB_DISABLE = 0, ++ MXL862XX_XPCS_LB_PCS_SERIAL = 1, ++ MXL862XX_XPCS_LB_PCS_PARALLEL = 2, ++ MXL862XX_XPCS_LB_PMA_SERIAL = 3, ++ MXL862XX_XPCS_LB_PMA_PARALLEL = 4, ++}; ++ ++/** ++ * enum mxl862xx_xpcs_reset_type - XPCS reset type ++ * @MXL862XX_XPCS_RESET_VR: vendor-specific reset (fast) ++ * @MXL862XX_XPCS_RESET_SOFT: PCS soft reset ++ * @MXL862XX_XPCS_RESET_HARD: full hardware reset ++ */ ++enum mxl862xx_xpcs_reset_type { ++ MXL862XX_XPCS_RESET_VR = 0, ++ MXL862XX_XPCS_RESET_SOFT = 1, ++ MXL862XX_XPCS_RESET_HARD = 2, ++}; ++ ++/** ++ * struct mxl862xx_xpcs_pcs_cfg - PCS configuration ++ * @port_id: XPCS port index (0 or 1) ++ * @interface: interface mode (enum mxl862xx_xpcs_if_mode) ++ * @neg_mode: negotiation mode (enum mxl862xx_xpcs_neg_mode) ++ * @permit_pause: allow pause to MAC ++ * @usx_lane_mode: USXGMII lane mode (0=single, 1=quad) ++ * @phy_side: PHY side (1) or MAC side (0) ++ * @advertising: CL37 advertisement word ++ * @result: firmware result (>0 AN restart needed, 0 no change, <0 error) ++ */ ++struct mxl862xx_xpcs_pcs_cfg { ++ u8 port_id:2; ++ u8 interface:6; ++ u8 neg_mode:2; ++ u8 permit_pause:1; ++ u8 usx_lane_mode:2; ++ u8 phy_side:1; ++ u8 __rsv:2; ++ __le16 advertising; ++ __le16 result; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_pcs_state - PCS link state ++ * @port_id: XPCS port index (0 or 1) ++ * @interface: interface mode (enum mxl862xx_xpcs_if_mode) ++ * @usx_lane_mode: USXGMII lane mode ++ * @usx_subport: USXGMII sub-port index (0-3) ++ * @link: link up ++ * @an_complete: auto-negotiation complete ++ * @duplex: duplex mode (enum mxl862xx_xpcs_duplex) ++ * @pcs_fault: PCS fault detected ++ * @pause: pause flags (bit 0 = symmetric, bit 1 = asymmetric) ++ * @speed: link speed in Mbps (enum mxl862xx_xpcs_speed) ++ * @lpa: raw link partner advertisement word ++ */ ++struct mxl862xx_xpcs_pcs_state { ++ u8 port_id:2; ++ u8 interface:6; ++ u8 usx_lane_mode:2; ++ u8 usx_subport:2; ++ u8 link:1; ++ u8 an_complete:1; ++ u8 duplex:1; ++ u8 pcs_fault:1; ++ u8 pause:2; ++ u8 __rsv:6; ++ u8 __pad; ++ __le16 speed; ++ __le16 lpa; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_pcs_power - PCS enable/disable ++ * @port_id: XPCS port index (0 or 1) ++ * @interface: interface mode (enum mxl862xx_xpcs_if_mode) ++ * @phy_side: PHY side (1) or MAC side (0) ++ * @result: firmware result ++ */ ++struct mxl862xx_xpcs_pcs_power { ++ u8 port_id:2; ++ u8 interface:6; ++ u8 phy_side:1; ++ u8 __rsv:7; ++ __le16 result; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_an_restart - AN restart parameters ++ * @port_id: XPCS port index (0 or 1) ++ * @interface: interface mode (enum mxl862xx_xpcs_if_mode) ++ * @usx_lane_mode: USXGMII lane mode ++ * @result: firmware result ++ */ ++struct mxl862xx_xpcs_an_restart { ++ u8 port_id:2; ++ u8 interface:6; ++ u8 usx_lane_mode:2; ++ u8 __rsv:6; ++ __le16 result; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_an_disable - AN disable parameters ++ * @port_id: XPCS port index ++ * @result: firmware result ++ */ ++struct mxl862xx_xpcs_an_disable { ++ u8 port_id; ++ u8 __pad; ++ __le16 result; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_force_speed - force PCS speed and duplex ++ * @port_id: XPCS port index ++ * @duplex: duplex mode (enum mxl862xx_xpcs_duplex) ++ * @speed: speed in Mbps (enum mxl862xx_xpcs_speed) ++ * @result: firmware result ++ */ ++struct mxl862xx_xpcs_force_speed { ++ u8 port_id; ++ u8 duplex; ++ __le16 speed; ++ __le16 result; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_loopback_cfg - loopback control ++ * @port_id: XPCS port index ++ * @mode: loopback mode (enum mxl862xx_xpcs_loopback_mode) ++ * @result: firmware result ++ */ ++struct mxl862xx_xpcs_loopback_cfg { ++ u8 port_id; ++ u8 mode; ++ __le16 result; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_reset_cfg - XPCS reset ++ * @port_id: XPCS port index ++ * @reset_type: reset type (enum mxl862xx_xpcs_reset_type) ++ * @result: firmware result ++ */ ++struct mxl862xx_xpcs_reset_cfg { ++ u8 port_id; ++ u8 reset_type; ++ __le16 result; ++} __packed; ++ + #endif /* __MXL862XX_API_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -25,6 +25,8 @@ + #define GPY_GPY2XX_MAGIC 0x1800 + #define SYS_MISC_MAGIC 0x1900 + ++#define MXL862XX_COMMON_PORTLINKCFGGET (MXL862XX_COMMON_MAGIC + 0x5) ++#define MXL862XX_COMMON_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x7) + #define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9) + #define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa) + #define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11) +@@ -71,6 +73,17 @@ + + #define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2) + ++#define MXL862XX_XPCS_MAGIC 0x1a00 ++#define MXL862XX_XPCS_PCS_CONFIG (MXL862XX_XPCS_MAGIC + 0x1) ++#define MXL862XX_XPCS_PCS_GET_STATE (MXL862XX_XPCS_MAGIC + 0x2) ++#define MXL862XX_XPCS_PCS_ENABLE (MXL862XX_XPCS_MAGIC + 0x3) ++#define MXL862XX_XPCS_PCS_DISABLE (MXL862XX_XPCS_MAGIC + 0x4) ++#define MXL862XX_XPCS_AN_RESTART (MXL862XX_XPCS_MAGIC + 0x5) ++#define MXL862XX_XPCS_AN_DISABLE (MXL862XX_XPCS_MAGIC + 0x6) ++#define MXL862XX_XPCS_FORCE_SPEED (MXL862XX_XPCS_MAGIC + 0x7) ++#define MXL862XX_XPCS_LOOPBACK (MXL862XX_XPCS_MAGIC + 0x8) ++#define MXL862XX_XPCS_RESET (MXL862XX_XPCS_MAGIC + 0x9) ++ + #define MMD_API_MAXIMUM_ID 0x7fff + + #endif /* __MXL862XX_CMD_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c +@@ -7,10 +7,14 @@ + * Copyright (C) 2025 Daniel Golle + */ + ++#include + #include + #include + + #include "mxl862xx.h" ++#include "mxl862xx-api.h" ++#include "mxl862xx-cmd.h" ++#include "mxl862xx-host.h" + #include "mxl862xx-phylink.h" + + void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, +@@ -19,8 +23,393 @@ void mxl862xx_phylink_get_caps(struct ds + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | + MAC_100 | MAC_1000 | MAC_2500FD; + +- __set_bit(PHY_INTERFACE_MODE_INTERNAL, +- config->supported_interfaces); ++ switch (port) { ++ case 1 ... 8: ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); ++ break; ++ case 9: ++ case 13: ++ __set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_10GKR, config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_USXGMII, config->supported_interfaces); ++ config->mac_capabilities |= MAC_10000FD | MAC_5000FD; ++ fallthrough; ++ case 10 ... 12: ++ case 14 ... 16: ++ __set_bit(PHY_INTERFACE_MODE_QSGMII, config->supported_interfaces); ++ break; ++ default: ++ break; ++ } ++} ++ ++static struct mxl862xx_pcs *pcs_to_mxl862xx_pcs(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mxl862xx_pcs, pcs); ++} ++ ++static int mxl862xx_xpcs_port_id(int port) ++{ ++ return port >= 13; ++} ++ ++static int mxl862xx_xpcs_if_mode(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ return MXL862XX_XPCS_IF_SGMII; ++ case PHY_INTERFACE_MODE_QSGMII: ++ return MXL862XX_XPCS_IF_QSGMII; ++ case PHY_INTERFACE_MODE_1000BASEX: ++ return MXL862XX_XPCS_IF_1000BASEX; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return MXL862XX_XPCS_IF_2500BASEX; ++ case PHY_INTERFACE_MODE_USXGMII: ++ return MXL862XX_XPCS_IF_USXGMII; ++ case PHY_INTERFACE_MODE_10GBASER: ++ return MXL862XX_XPCS_IF_10GBASER; ++ case PHY_INTERFACE_MODE_10GKR: ++ return MXL862XX_XPCS_IF_10GKR; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int mxl862xx_xpcs_neg_mode(unsigned int neg_mode) ++{ ++ if (!(neg_mode & PHYLINK_PCS_NEG_INBAND)) ++ return MXL862XX_XPCS_NEG_NONE; ++ if (neg_mode & PHYLINK_PCS_NEG_ENABLED) ++ return MXL862XX_XPCS_NEG_INBAND_AN_ON; ++ return MXL862XX_XPCS_NEG_INBAND_AN_OFF; ++} ++ ++static struct mxl862xx_xpcs_signal_detect ++mxl862xx_xpcs_signal_detect(struct mxl862xx_priv *priv, int port_id) ++{ ++ struct mxl862xx_xpcs_signal_detect sd = { .port_id = port_id }; ++ ++ MXL862XX_API_READ(priv, MXL862XX_XPCS_SIGNAL_DETECT, sd); ++ ++ return sd; ++} ++ ++static int mxl862xx_xpcs_poll_ready(struct mxl862xx_priv *priv, int port_id) ++{ ++ struct mxl862xx_xpcs_signal_detect sd; ++ int ret; ++ ++ ret = read_poll_timeout(mxl862xx_xpcs_signal_detect, sd, ++ !sd.in_reset, 50000, 1000000, ++ false, priv, port_id); ++ if (ret) ++ dev_warn(priv->ds->dev, "XPCS%d ready timeout\n", port_id); ++ ++ return ret; ++} ++ ++static void mxl862xx_pcs_disable(struct phylink_pcs *pcs) ++{ ++ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); ++ struct mxl862xx_priv *priv = mpcs->priv; ++ int port = mpcs->port; ++ struct mxl862xx_xpcs_pcs_power pwr = {}; ++ ++ if (port != 9 && port != 13) ++ return; ++ ++ pwr.port_id = mxl862xx_xpcs_port_id(port); ++ ++ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_DISABLE, pwr); ++ mpcs->enabled = false; ++} ++ ++static void mxl862xx_pcs_pre_config(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); ++ struct mxl862xx_priv *priv = mpcs->priv; ++ int port = mpcs->port; ++ struct mxl862xx_xpcs_pcs_power pwr = {}; ++ struct mxl862xx_xpcs_reset_cfg rst = {}; ++ int port_id, if_mode; ++ ++ if (port != 9 && port != 13) ++ return; ++ ++ if_mode = mxl862xx_xpcs_if_mode(interface); ++ if (if_mode < 0) ++ return; ++ ++ port_id = mxl862xx_xpcs_port_id(port); ++ ++ /* Full reset only if PCS is already running (not after a clean disable, ++ * which already asserts hardware reset via XPCS_PCS_DISABLE). ++ */ ++ if (mpcs->enabled) { ++ rst.port_id = port_id; ++ rst.reset_type = MXL862XX_XPCS_RESET_HARD; ++ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_RESET, rst); ++ mxl862xx_xpcs_poll_ready(priv, port_id); ++ } ++ ++ pwr.port_id = port_id; ++ pwr.interface = if_mode; ++ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_ENABLE, pwr); ++ mxl862xx_xpcs_poll_ready(priv, port_id); ++ mpcs->enabled = true; ++} ++ ++static int mxl862xx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); ++ struct mxl862xx_priv *priv = mpcs->priv; ++ int port = mpcs->port; ++ struct mxl862xx_xpcs_pcs_cfg cfg = {}; ++ int if_mode, ret; ++ ++ /* Sub-interfaces are set up implicitly by the main interface */ ++ if (port != 9 && port != 13) ++ return 0; ++ ++ if_mode = mxl862xx_xpcs_if_mode(interface); ++ if (if_mode < 0) { ++ dev_err(priv->ds->dev, "unsupported interface: %s\n", ++ phy_modes(interface)); ++ return if_mode; ++ } ++ ++ mpcs->if_mode = if_mode; ++ ++ cfg.port_id = mxl862xx_xpcs_port_id(port); ++ cfg.interface = if_mode; ++ cfg.neg_mode = mxl862xx_xpcs_neg_mode(neg_mode); ++ cfg.permit_pause = permit_pause_to_mac ? 1 : 0; ++ ++ if (neg_mode & PHYLINK_PCS_NEG_INBAND) { ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ cfg.advertising = cpu_to_le16( ++ linkmode_adv_to_mii_adv_x(advertising, ++ ETHTOOL_LINK_MODE_1000baseX_Full_BIT)); ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ cfg.advertising = cpu_to_le16(ADVERTISE_SGMII); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_CONFIG, cfg); ++ if (ret) ++ return ret; ++ ++ /* result > 0 means AN restart is needed */ ++ return le16_to_cpu(cfg.result) > 0 ? 1 : 0; ++} ++ ++static void mxl862xx_xpcs_decode_speed(u16 fw_speed, ++ struct phylink_link_state *state) ++{ ++ switch (fw_speed) { ++ case MXL862XX_XPCS_SPEED_10: ++ state->speed = SPEED_10; ++ break; ++ case MXL862XX_XPCS_SPEED_100: ++ state->speed = SPEED_100; ++ break; ++ case MXL862XX_XPCS_SPEED_1000: ++ state->speed = SPEED_1000; ++ break; ++ case MXL862XX_XPCS_SPEED_2500: ++ state->speed = SPEED_2500; ++ break; ++ case MXL862XX_XPCS_SPEED_5000: ++ state->speed = SPEED_5000; ++ break; ++ case MXL862XX_XPCS_SPEED_10000: ++ state->speed = SPEED_10000; ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ break; ++ } ++ ++ state->duplex = DUPLEX_FULL; ++} ++ ++static void mxl862xx_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mxl862xx_priv *priv = pcs_to_mxl862xx_pcs(pcs)->priv; ++ int port = pcs_to_mxl862xx_pcs(pcs)->port; ++ struct mxl862xx_xpcs_pcs_state st = {}; ++ int if_mode, ret; ++ u16 fw_speed, lpa, bmsr; ++ ++ if_mode = mxl862xx_xpcs_if_mode(state->interface); ++ if (if_mode < 0) ++ return; ++ ++ st.port_id = mxl862xx_xpcs_port_id(port); ++ st.interface = if_mode; ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_GET_STATE, st); ++ if (ret) ++ return; ++ ++ fw_speed = le16_to_cpu(st.speed); ++ lpa = le16_to_cpu(st.lpa); ++ ++ state->link = st.link && !st.pcs_fault; ++ if (!state->link) ++ return; ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ /* Synthesize BMSR from firmware state and use phylink's ++ * standard CL37/SGMII decoders for LPA, pause, and speed. ++ */ ++ bmsr = BMSR_LSTATUS; ++ if (st.an_complete) ++ bmsr |= BMSR_ANEGCOMPLETE; ++ phylink_mii_c22_pcs_decode_state(state, bmsr, lpa); ++ ++ /* Override speed/duplex with firmware's resolved values ++ * for downshift detection. ++ */ ++ mxl862xx_xpcs_decode_speed(fw_speed, state); ++ state->duplex = st.duplex ? DUPLEX_FULL : DUPLEX_HALF; ++ break; ++ ++ case PHY_INTERFACE_MODE_USXGMII: ++ state->an_complete = st.an_complete; ++ phylink_decode_usxgmii_word(state, lpa); ++ ++ /* Override with firmware's resolved values */ ++ mxl862xx_xpcs_decode_speed(fw_speed, state); ++ state->duplex = st.duplex ? DUPLEX_FULL : DUPLEX_HALF; ++ break; ++ ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_10GKR: ++ mxl862xx_xpcs_decode_speed(fw_speed, state); ++ break; ++ ++ default: ++ state->link = false; ++ break; ++ } ++} ++ ++static void mxl862xx_pcs_an_restart(struct phylink_pcs *pcs) ++{ ++ struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); ++ struct mxl862xx_priv *priv = mpcs->priv; ++ int port = mpcs->port; ++ struct mxl862xx_xpcs_an_restart an = {}; ++ ++ if (port != 9 && port != 13) ++ return; ++ ++ an.port_id = mxl862xx_xpcs_port_id(port); ++ an.interface = mpcs->if_mode; ++ ++ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_AN_RESTART, an); ++} ++ ++static void mxl862xx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, ++ phy_interface_t interface, int speed, ++ int duplex) ++{ ++ struct mxl862xx_priv *priv = pcs_to_mxl862xx_pcs(pcs)->priv; ++ int port = pcs_to_mxl862xx_pcs(pcs)->port; ++ struct mxl862xx_xpcs_force_speed fs = {}; ++ ++ /* Only SGMII needs explicit speed forcing */ ++ if (interface != PHY_INTERFACE_MODE_SGMII) ++ return; ++ ++ if (port != 9 && port != 13) ++ return; ++ ++ fs.port_id = mxl862xx_xpcs_port_id(port); ++ fs.duplex = (duplex == DUPLEX_FULL) ? MXL862XX_XPCS_DUPLEX_FULL : ++ MXL862XX_XPCS_DUPLEX_HALF; ++ fs.speed = cpu_to_le16(speed); ++ ++ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_FORCE_SPEED, fs); ++} ++ ++static unsigned int mxl862xx_pcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_10GBASER: ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE | ++ LINK_INBAND_BYPASS; ++ case PHY_INTERFACE_MODE_10GKR: ++ return LINK_INBAND_ENABLE | LINK_INBAND_BYPASS; ++ default: ++ return 0; ++ } ++} ++ ++static const struct phylink_pcs_ops mxl862xx_pcs_ops = { ++ .pcs_disable = mxl862xx_pcs_disable, ++ .pcs_pre_config = mxl862xx_pcs_pre_config, ++ .pcs_config = mxl862xx_pcs_config, ++ .pcs_get_state = mxl862xx_pcs_get_state, ++ .pcs_an_restart = mxl862xx_pcs_an_restart, ++ .pcs_link_up = mxl862xx_pcs_link_up, ++ .pcs_inband_caps = mxl862xx_pcs_inband_caps, ++}; ++ ++void mxl862xx_setup_pcs(struct mxl862xx_priv *priv, struct mxl862xx_pcs *pcs, ++ int port) ++{ ++ pcs->priv = priv; ++ pcs->port = port; ++ ++ pcs->pcs.ops = &mxl862xx_pcs_ops; ++ pcs->pcs.poll = true; ++} ++ ++static struct phylink_pcs * ++mxl862xx_phylink_mac_select_pcs(struct phylink_config *config, ++ phy_interface_t interface) ++{ ++ struct dsa_port *dp = dsa_phylink_to_port(config); ++ struct mxl862xx_priv *priv = dp->ds->priv; ++ int port = dp->index; ++ ++ if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80)) ++ return NULL; ++ ++ switch (port) { ++ case 9 ... 16: ++ return &priv->serdes_ports[port - 9].pcs; ++ default: ++ return NULL; ++ } + } + + static void mxl862xx_phylink_mac_config(struct phylink_config *config, +@@ -48,4 +437,5 @@ const struct phylink_mac_ops mxl862xx_ph + .mac_config = mxl862xx_phylink_mac_config, + .mac_link_down = mxl862xx_phylink_mac_link_down, + .mac_link_up = mxl862xx_phylink_mac_link_up, ++ .mac_select_pcs = mxl862xx_phylink_mac_select_pcs, + }; +--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h +@@ -10,5 +10,7 @@ + extern const struct phylink_mac_ops mxl862xx_phylink_mac_ops; + void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config); ++void mxl862xx_setup_pcs(struct mxl862xx_priv *priv, struct mxl862xx_pcs *pcs, ++ int port); + + #endif /* __MXL862XX_PHYLINK_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -729,7 +729,7 @@ static int mxl862xx_setup(struct dsa_swi + int n_user_ports = 0, max_vlans; + int ingress_finals, vid_rules; + struct dsa_port *dp; +- int ret; ++ int ret, i; + + ret = mxl862xx_reset(priv); + if (ret) +@@ -739,6 +739,9 @@ static int mxl862xx_setup(struct dsa_swi + if (ret) + return ret; + ++ for (i = 0; i < 8; i++) ++ mxl862xx_setup_pcs(priv, &priv->serdes_ports[i], i + 9); ++ + /* Calculate Extended VLAN block sizes. + * With VLAN Filter handling VID membership checks: + * Ingress: only final catchall rules (PVID insertion, 802.1Q +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -247,6 +247,22 @@ struct mxl862xx_port { + }; + + /** ++ * struct mxl862xx_pcs - link SerDes interfaces to bridge ports ++ * @pcs: &struct phylink_pcs instance ++ * @priv: pointer to &struct mxl862xx_priv ++ * @port: bridge port index ++ * @if_mode: cached firmware interface mode (enum mxl862xx_xpcs_if_mode) ++ * @enabled: true if the PCS/SerDes is currently powered up ++ */ ++struct mxl862xx_pcs { ++ struct phylink_pcs pcs; ++ struct mxl862xx_priv *priv; ++ int port; ++ int if_mode; ++ bool enabled; ++}; ++ ++/** + * union mxl862xx_fw_version - firmware version for comparison and display + * @major: firmware major version + * @minor: firmware minor version +@@ -291,6 +307,8 @@ union mxl862xx_fw_version { + * flooding) + * @fw_version: cached firmware version, populated at probe and + * compared with MXL862XX_FW_VER_MIN() ++ * @serdes_ports: SerDes interfaces incl. sub-interfaces in case of ++ * 10G_QXGMII + * @ports: per-port state, indexed by switch port number + * @bridges: maps DSA bridge number to firmware bridge ID; + * zero means no firmware bridge allocated for that +@@ -309,6 +327,7 @@ struct mxl862xx_priv { + unsigned long crc_err; + u16 drop_meter; + union mxl862xx_fw_version fw_version; ++ struct mxl862xx_pcs serdes_ports[8]; + struct mxl862xx_port ports[MXL862XX_MAX_PORTS]; + u16 bridges[MXL862XX_MAX_BRIDGES + 1]; + u16 evlan_ingress_size; diff --git a/target/linux/generic/pending-6.12/760-12-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch b/target/linux/generic/pending-6.12/760-12-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch new file mode 100644 index 00000000000..5f1e1bb3e59 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-12-net-dsa-mxl862xx-add-SerDes-ethtool-statistics.patch @@ -0,0 +1,268 @@ +From d40565e2e00fc2c8f04b9c571fcbea2f146db844 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 18:14:33 +0000 +Subject: [PATCH 21/35] net: dsa: mxl862xx: add SerDes ethtool statistics + +Expose SerDes equalization and signal detect parameters as ethtool +statistics on ports 9-16 (XPCS-backed ports). Uses the XPCS EQ_GET +and SIGNAL_DETECT firmware commands to read TX/RX equalization +coefficients, DFE taps, and link-level signal status. + +The 19 additional stats (serdes_tx_*, serdes_rx_*, serdes_pma_link, +serdes_link_fault, serdes_in_reset) are appended after the standard +RMON counters and gated on firmware >= 1.0.80. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 88 +++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 2 + + drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 93 +++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 3 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 6 +- + 5 files changed, 191 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -1812,4 +1812,92 @@ struct mxl862xx_xpcs_reset_cfg { + __le16 result; + } __packed; + ++/** ++ * struct mxl862xx_xpcs_eq_item - single equalization parameter ++ * @value: current initial value ++ * @ovrd: override value ++ * @ovrd_en: override enable flag ++ */ ++struct mxl862xx_xpcs_eq_item { ++ u8 value; ++ u8 ovrd; ++ u8 ovrd_en; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_tx_eq_info - TX equalization status ++ * @main: TX main cursor (0-63) ++ * @pre: TX pre-cursor (0-63) ++ * @post: TX post-cursor (0-63) ++ * @iboost_lvl: TX iboost level (0-15) ++ * @vboost_lvl: TX vboost level (0-7) ++ * @vboost_en: TX vboost enable (0-1) ++ */ ++struct mxl862xx_xpcs_tx_eq_info { ++ struct mxl862xx_xpcs_eq_item main; ++ struct mxl862xx_xpcs_eq_item pre; ++ struct mxl862xx_xpcs_eq_item post; ++ struct mxl862xx_xpcs_eq_item iboost_lvl; ++ struct mxl862xx_xpcs_eq_item vboost_lvl; ++ struct mxl862xx_xpcs_eq_item vboost_en; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_rx_eq_info - RX equalization status ++ * @att_lvl: RX attenuation level (0-7) ++ * @vga1_gain: RX VGA1 gain (0-7) ++ * @vga2_gain: RX VGA2 gain (0-7) ++ * @ctle_boost: RX CTLE boost (0-31) ++ * @ctle_pole: RX CTLE pole (0-3) ++ * @dfe_tap1: RX DFE tap1 (0-255) ++ * @dfe_bypass: RX DFE bypass (0-1) ++ * @adapt_mode: RX adapt mode (0-3) ++ * @adapt_sel: RX adapt select (0-1) ++ */ ++struct mxl862xx_xpcs_rx_eq_info { ++ struct mxl862xx_xpcs_eq_item att_lvl; ++ struct mxl862xx_xpcs_eq_item vga1_gain; ++ struct mxl862xx_xpcs_eq_item vga2_gain; ++ struct mxl862xx_xpcs_eq_item ctle_boost; ++ struct mxl862xx_xpcs_eq_item ctle_pole; ++ struct mxl862xx_xpcs_eq_item dfe_tap1; ++ struct mxl862xx_xpcs_eq_item dfe_bypass; ++ struct mxl862xx_xpcs_eq_item adapt_mode; ++ struct mxl862xx_xpcs_eq_item adapt_sel; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_eq_get - EQ get request/response ++ * @port_id: XPCS port index (0 or 1) ++ * @result: firmware result ++ * @tx: TX equalization info ++ * @rx: RX equalization info ++ */ ++struct mxl862xx_xpcs_eq_get { ++ u8 port_id; ++ __le16 result; ++ struct mxl862xx_xpcs_tx_eq_info tx; ++ struct mxl862xx_xpcs_rx_eq_info rx; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_signal_detect - signal detect status ++ * @port_id: XPCS port index (0 or 1) ++ * @rx_signal: RX signal detected ++ * @pma_link: PMA link up ++ * @link_fault: PCS link fault ++ * @in_reset: XPCS in reset ++ * @result: firmware result ++ */ ++struct mxl862xx_xpcs_signal_detect { ++ u8 port_id:2; ++ u8 rx_signal:1; ++ u8 pma_link:1; ++ u8 link_fault:1; ++ u8 in_reset:1; ++ u8 __rsv:2; ++ u8 __pad; ++ __le16 result; ++} __packed; ++ + #endif /* __MXL862XX_API_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -83,6 +83,8 @@ + #define MXL862XX_XPCS_FORCE_SPEED (MXL862XX_XPCS_MAGIC + 0x7) + #define MXL862XX_XPCS_LOOPBACK (MXL862XX_XPCS_MAGIC + 0x8) + #define MXL862XX_XPCS_RESET (MXL862XX_XPCS_MAGIC + 0x9) ++#define MXL862XX_XPCS_EQ_GET (MXL862XX_XPCS_MAGIC + 0xc) ++#define MXL862XX_XPCS_SIGNAL_DETECT (MXL862XX_XPCS_MAGIC + 0xd) + + #define MMD_API_MAXIMUM_ID 0x7fff + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c +@@ -439,3 +439,96 @@ const struct phylink_mac_ops mxl862xx_ph + .mac_link_up = mxl862xx_phylink_mac_link_up, + .mac_select_pcs = mxl862xx_phylink_mac_select_pcs, + }; ++ ++/* --- SerDes ethtool statistics --- */ ++ ++static const char mxl862xx_serdes_stats[][ETH_GSTRING_LEN] = { ++ "serdes_tx_main", ++ "serdes_tx_pre", ++ "serdes_tx_post", ++ "serdes_tx_iboost", ++ "serdes_tx_vboost", ++ "serdes_tx_vboost_en", ++ "serdes_rx_att", ++ "serdes_rx_vga1", ++ "serdes_rx_vga2", ++ "serdes_rx_ctle_boost", ++ "serdes_rx_ctle_pole", ++ "serdes_rx_dfe_tap1", ++ "serdes_rx_dfe_bypass", ++ "serdes_rx_adapt_mode", ++ "serdes_rx_adapt_sel", ++ "serdes_rx_signal", ++ "serdes_pma_link", ++ "serdes_link_fault", ++ "serdes_in_reset", ++}; ++ ++static bool mxl862xx_port_has_serdes_stats(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ ++ return port >= 9 && port <= 16 && ++ MXL862XX_FW_VER_MIN(priv, 1, 0, 80); ++} ++ ++int mxl862xx_serdes_stats_count(struct dsa_switch *ds, int port) ++{ ++ if (mxl862xx_port_has_serdes_stats(ds, port)) ++ return ARRAY_SIZE(mxl862xx_serdes_stats); ++ ++ return 0; ++} ++ ++void mxl862xx_serdes_get_strings(struct dsa_switch *ds, int port, u8 *data) ++{ ++ int i; ++ ++ if (!mxl862xx_port_has_serdes_stats(ds, port)) ++ return; ++ ++ for (i = 0; i < ARRAY_SIZE(mxl862xx_serdes_stats); i++) ++ ethtool_puts(&data, mxl862xx_serdes_stats[i]); ++} ++ ++void mxl862xx_serdes_get_stats(struct dsa_switch *ds, int port, u64 *data) ++{ ++ struct mxl862xx_xpcs_eq_get eq = { ++ .port_id = mxl862xx_xpcs_port_id(port), ++ }; ++ struct mxl862xx_xpcs_signal_detect sig = {}; ++ ++ if (!mxl862xx_port_has_serdes_stats(ds, port)) ++ return; ++ ++ sig.port_id = mxl862xx_xpcs_port_id(port); ++ ++ if (!MXL862XX_API_READ(ds->priv, MXL862XX_XPCS_EQ_GET, eq)) { ++ *data++ = eq.tx.main.value; ++ *data++ = eq.tx.pre.value; ++ *data++ = eq.tx.post.value; ++ *data++ = eq.tx.iboost_lvl.value; ++ *data++ = eq.tx.vboost_lvl.value; ++ *data++ = eq.tx.vboost_en.value; ++ *data++ = eq.rx.att_lvl.value; ++ *data++ = eq.rx.vga1_gain.value; ++ *data++ = eq.rx.vga2_gain.value; ++ *data++ = eq.rx.ctle_boost.value; ++ *data++ = eq.rx.ctle_pole.value; ++ *data++ = eq.rx.dfe_tap1.value; ++ *data++ = eq.rx.dfe_bypass.value; ++ *data++ = eq.rx.adapt_mode.value; ++ *data++ = eq.rx.adapt_sel.value; ++ } else { ++ data += 15; ++ } ++ ++ if (!MXL862XX_API_READ(ds->priv, MXL862XX_XPCS_SIGNAL_DETECT, sig)) { ++ *data++ = sig.rx_signal; ++ *data++ = sig.pma_link; ++ *data++ = sig.link_fault; ++ *data++ = sig.in_reset; ++ } else { ++ data += 4; ++ } ++} +--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h +@@ -12,5 +12,8 @@ void mxl862xx_phylink_get_caps(struct ds + struct phylink_config *config); + void mxl862xx_setup_pcs(struct mxl862xx_priv *priv, struct mxl862xx_pcs *pcs, + int port); ++int mxl862xx_serdes_stats_count(struct dsa_switch *ds, int port); ++void mxl862xx_serdes_get_strings(struct dsa_switch *ds, int port, u8 *data); ++void mxl862xx_serdes_get_stats(struct dsa_switch *ds, int port, u64 *data); + + #endif /* __MXL862XX_PHYLINK_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -2007,6 +2007,8 @@ static void mxl862xx_get_strings(struct + + for (i = 0; i < ARRAY_SIZE(mxl862xx_mib); i++) + ethtool_puts(&data, mxl862xx_mib[i].name); ++ ++ mxl862xx_serdes_get_strings(ds, port, data); + } + + static int mxl862xx_get_sset_count(struct dsa_switch *ds, int port, int sset) +@@ -2014,7 +2016,7 @@ static int mxl862xx_get_sset_count(struc + if (sset != ETH_SS_STATS) + return 0; + +- return ARRAY_SIZE(mxl862xx_mib); ++ return ARRAY_SIZE(mxl862xx_mib) + mxl862xx_serdes_stats_count(ds, port); + } + + static int mxl862xx_read_rmon(struct dsa_switch *ds, int port, +@@ -2050,6 +2052,8 @@ static void mxl862xx_get_ethtool_stats(s + else + *data++ = le64_to_cpu(*(__le64 *)field); + } ++ ++ mxl862xx_serdes_get_stats(ds, port, data); + } + + static void mxl862xx_get_eth_mac_stats(struct dsa_switch *ds, int port, diff --git a/target/linux/generic/pending-6.12/760-13-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch b/target/linux/generic/pending-6.12/760-13-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch new file mode 100644 index 00000000000..1da11ae9c07 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-13-net-dsa-mxl862xx-add-SerDes-self-test-via-PRBS-and-B.patch @@ -0,0 +1,208 @@ +From 54dd5fabc543f8538202367a863eb0e9161bacab Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 18:15:32 +0000 +Subject: [PATCH 22/35] net: dsa: mxl862xx: add SerDes self-test via PRBS and + BERT + +Implement the dsa_switch_ops.self_test callback for SerDes ports +(9-16). Two loopback tests are run: + + 1. PCS-level PRBS31: enables TX/RX PRBS31 pattern at the PCS layer, + waits 100ms, then reads the error counter. + 2. SerDes-level BERT PRBS31: enables TX/RX BERT with PRBS31 pattern + at the SerDes layer, waits 100ms, then reads the error counter. + +Both tests clean up (disable pattern generators) regardless of outcome. +Gated on firmware >= 1.0.80 via mxl862xx_port_has_serdes_stats(). + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 45 +++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 2 + + drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 85 +++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-phylink.h | 3 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 1 + + 5 files changed, 136 insertions(+) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -1900,4 +1900,49 @@ struct mxl862xx_xpcs_signal_detect { + __le16 result; + } __packed; + ++/** ++ * struct mxl862xx_xpcs_prbs_cfg - PCS-level PRBS31 test pattern ++ * @port_id: XPCS port index (0 or 1) ++ * @tx_en: TX PRBS31 enable ++ * @rx_en: RX PRBS31 enable ++ * @read_err: read error count ++ * @rx_err_cnt: RX PRBS31 error count (valid when read_err=1) ++ * @result: firmware result ++ */ ++struct mxl862xx_xpcs_prbs_cfg { ++ u8 port_id:2; ++ u8 tx_en:1; ++ u8 rx_en:1; ++ u8 read_err:1; ++ u8 __rsv:3; ++ u8 __pad; ++ __le16 rx_err_cnt; ++ __le16 result; ++} __packed; ++ ++/** ++ * struct mxl862xx_xpcs_bert_cfg - SerDes-level BERT test pattern ++ * @port_id: XPCS port index (0 or 1) ++ * @tx_en: TX BERT enable ++ * @rx_en: RX BERT enable ++ * @read_err: read RX error count ++ * @clear_err: clear RX error counter ++ * @insert_err: insert one TX error ++ * @pattern: PRBS pattern type (1-7; 0 = disable) ++ * @rx_err_cnt: RX BERT error count (valid when read_err=1) ++ * @result: firmware result ++ */ ++struct mxl862xx_xpcs_bert_cfg { ++ u8 port_id:2; ++ u8 tx_en:1; ++ u8 rx_en:1; ++ u8 read_err:1; ++ u8 clear_err:1; ++ u8 insert_err:1; ++ u8 __rsv:1; ++ u8 pattern; ++ __le16 rx_err_cnt; ++ __le16 result; ++} __packed; ++ + #endif /* __MXL862XX_API_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -83,6 +83,8 @@ + #define MXL862XX_XPCS_FORCE_SPEED (MXL862XX_XPCS_MAGIC + 0x7) + #define MXL862XX_XPCS_LOOPBACK (MXL862XX_XPCS_MAGIC + 0x8) + #define MXL862XX_XPCS_RESET (MXL862XX_XPCS_MAGIC + 0x9) ++#define MXL862XX_XPCS_PRBS_CFG (MXL862XX_XPCS_MAGIC + 0xa) ++#define MXL862XX_XPCS_BERT_CFG (MXL862XX_XPCS_MAGIC + 0xb) + #define MXL862XX_XPCS_EQ_GET (MXL862XX_XPCS_MAGIC + 0xc) + #define MXL862XX_XPCS_SIGNAL_DETECT (MXL862XX_XPCS_MAGIC + 0xd) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c +@@ -532,3 +532,88 @@ void mxl862xx_serdes_get_stats(struct ds + data += 4; + } + } ++ ++void mxl862xx_serdes_self_test(struct dsa_switch *ds, int port, ++ struct ethtool_test *etest, u64 *data) ++{ ++ struct mxl862xx_xpcs_prbs_cfg prbs = {}; ++ struct mxl862xx_xpcs_bert_cfg bert = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ int xpcs_id = mxl862xx_xpcs_port_id(port); ++ int i = 0; ++ int ret; ++ ++ if (!mxl862xx_port_has_serdes_stats(ds, port)) ++ return; ++ ++ /* Test 1: PCS PRBS31 */ ++ prbs.port_id = xpcs_id; ++ prbs.tx_en = 1; ++ prbs.rx_en = 1; ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PRBS_CFG, prbs); ++ if (ret) { ++ data[i++] = 1; ++ goto skip_prbs; ++ } ++ ++ msleep(100); ++ ++ memset(&prbs, 0, sizeof(prbs)); ++ prbs.port_id = xpcs_id; ++ prbs.read_err = 1; ++ ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PRBS_CFG, prbs); ++ ++ /* Disable PRBS */ ++ memset(&prbs, 0, sizeof(prbs)); ++ prbs.port_id = xpcs_id; ++ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PRBS_CFG, prbs); ++ ++ if (ret) { ++ data[i++] = 1; ++ } else { ++ data[i] = le16_to_cpu(prbs.rx_err_cnt) ? 1 : 0; ++ if (data[i]) ++ etest->flags |= ETH_TEST_FL_FAILED; ++ i++; ++ } ++ ++skip_prbs: ++ /* Test 2: SerDes BERT PRBS31 -- clear error counter first */ ++ bert.port_id = xpcs_id; ++ bert.clear_err = 1; ++ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_BERT_CFG, bert); ++ ++ /* Enable BERT with PRBS31 pattern */ ++ memset(&bert, 0, sizeof(bert)); ++ bert.port_id = xpcs_id; ++ bert.tx_en = 1; ++ bert.rx_en = 1; ++ bert.pattern = 6; /* PRBS31 */ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_XPCS_BERT_CFG, bert); ++ if (ret) { ++ data[i++] = 1; ++ return; ++ } ++ ++ msleep(100); ++ ++ /* Read error count */ ++ memset(&bert, 0, sizeof(bert)); ++ bert.port_id = xpcs_id; ++ bert.read_err = 1; ++ ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_BERT_CFG, bert); ++ ++ /* Disable BERT */ ++ memset(&bert, 0, sizeof(bert)); ++ bert.port_id = xpcs_id; ++ MXL862XX_API_WRITE(priv, MXL862XX_XPCS_BERT_CFG, bert); ++ ++ if (ret) { ++ data[i++] = 1; ++ } else { ++ data[i] = le16_to_cpu(bert.rx_err_cnt) ? 1 : 0; ++ if (data[i]) ++ etest->flags |= ETH_TEST_FL_FAILED; ++ i++; ++ } ++} +--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h +@@ -3,6 +3,7 @@ + #ifndef __MXL862XX_PHYLINK_H + #define __MXL862XX_PHYLINK_H + ++#include + #include + + #include "mxl862xx.h" +@@ -15,5 +16,7 @@ void mxl862xx_setup_pcs(struct mxl862xx_ + int mxl862xx_serdes_stats_count(struct dsa_switch *ds, int port); + void mxl862xx_serdes_get_strings(struct dsa_switch *ds, int port, u8 *data); + void mxl862xx_serdes_get_stats(struct dsa_switch *ds, int port, u64 *data); ++void mxl862xx_serdes_self_test(struct dsa_switch *ds, int port, ++ struct ethtool_test *etest, u64 *data); + + #endif /* __MXL862XX_PHYLINK_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -2286,6 +2286,7 @@ static const struct dsa_switch_ops mxl86 + .get_eth_ctrl_stats = mxl862xx_get_eth_ctrl_stats, + .get_pause_stats = mxl862xx_get_pause_stats, + .get_stats64 = mxl862xx_get_stats64, ++ .self_test = mxl862xx_serdes_self_test, + }; + + static int mxl862xx_probe(struct mdio_device *mdiodev) diff --git a/target/linux/generic/pending-6.12/760-14-net-dsa-mxl862xx-trap-link-local-frames-to-the-CPU-p.patch b/target/linux/generic/pending-6.12/760-14-net-dsa-mxl862xx-trap-link-local-frames-to-the-CPU-p.patch new file mode 100644 index 00000000000..385e44f480c --- /dev/null +++ b/target/linux/generic/pending-6.12/760-14-net-dsa-mxl862xx-trap-link-local-frames-to-the-CPU-p.patch @@ -0,0 +1,831 @@ +From dd62e68cd0bd29934c3efbce687d5e103cc4b331 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 18:51:13 +0000 +Subject: [PATCH 23/35] net: dsa: mxl862xx: trap link-local frames to the CPU + port + +Install per-CTP PCE rules on each user port that trap IEEE 802.1D +link-local frames (01:80:c2:00:00:0x) to the CPU port via an +explicit forwarding portmap with cross-state enabled, ensuring the +frames reach the host even when the bridge port is in BLOCKING or +LEARNING state. + +Add the PCE rule firmware API structures, command definitions, and +the rule block allocation interface. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 684 ++++++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 5 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 69 +++ + 3 files changed, 758 insertions(+) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -1420,6 +1420,690 @@ struct mxl862xx_port_link_cfg { + u8 lpi; + } __packed; + ++/* PCE (Packet Classification Engine) rule structures. ++ * ++ * Binary layout must exactly match the firmware's GSW_PCE_rule_t ++ * (gsw_flow.h, packed little-endian). The firmware deserializes ++ * the structure directly from the MDIO data buffer produced by ++ * mxl862xx_api_wrap(). ++ */ ++ ++/** ++ * union mxl862xx_ip - IPv4 or IPv6 address ++ * @ipv4: IPv4 address in little-endian ++ * @ipv6: IPv6 address as array of little-endian 16-bit words ++ */ ++union mxl862xx_ip { ++ __le32 ipv4; ++ __le16 ipv6[8]; ++} __packed; ++ ++/** ++ * struct mxl862xx_pce_pattern - PCE rule match pattern ++ * ++ * Every field must remain in the order shown; the firmware ++ * interprets the buffer positionally. ++ * ++ * @index: PCE rule index (0..511) ++ * @dst_ip_mask: Destination IP nibble mask (outer) ++ * @inner_dst_ip_mask: Inner destination IP nibble mask ++ * @src_ip_mask: Source IP nibble mask (outer) ++ * @inner_src_ip_mask: Inner source IP nibble mask ++ * @sub_if_id: Incoming sub-interface ID value ++ * @pkt_lng: Packet length in bytes ++ * @pkt_lng_range: Packet length range upper bound ++ * @mac_dst_mask: Destination MAC nibble mask ++ * @mac_src_mask: Source MAC nibble mask ++ * @app_data_msb: MSB application field (first 2 bytes after IP header, ++ * typically TCP/UDP source port) ++ * @app_mask_range_msb: MSB application mask or range value ++ * @ether_type: EtherType value to match ++ * @ether_type_mask: EtherType nibble mask ++ * @session_id: PPPoE session ID ++ * @ppp_protocol: PPP protocol value ++ * @ppp_protocol_mask: PPP protocol bit mask ++ * @vid: CTAG VLAN ID (inner VLAN) ++ * @slan_vid: STAG VLAN ID (outer VLAN) ++ * @flex_field4_value: Flexible field 4 match value ++ * @flex_field4_mask_range: Flexible field 4 mask or range ++ * @flex_field3_value: Flexible field 3 match value ++ * @flex_field3_mask_range: Flexible field 3 mask or range ++ * @flex_field1_value: Flexible field 1 match value ++ * @flex_field1_mask_range: Flexible field 1 mask or range ++ * @outer_vid_range: Outer VLAN ID range ++ * @payload1: Payload-1 value (16-bit) ++ * @payload1_mask: Payload-1 bit mask ++ * @payload2: Payload-2 value (16-bit) ++ * @payload2_mask: Payload-2 bit mask ++ * @parser_flag_lsb: Parser flag LSW value (bits 15:0) ++ * @parser_flag_lsb_mask: Parser flag LSW mask (1 = masked out) ++ * @parser_flag_msb: Parser flag MSW value (bits 31:16) ++ * @parser_flag_msb_mask: Parser flag MSW mask (1 = masked out) ++ * @parser_flag1_lsb: Parser flag1 LSW value (bits 47:32) ++ * @parser_flag1_lsb_mask: Parser flag1 LSW mask ++ * @parser_flag1_msb: Parser flag1 MSW value (bits 63:48) ++ * @parser_flag1_msb_mask: Parser flag1 MSW mask ++ * @app_data_lsb: LSB application field (next 2 bytes after @app_data_msb, ++ * typically TCP/UDP destination port) ++ * @app_mask_range_lsb: LSB application mask or range value ++ * @insertion_flag: CPU-inserted packet flag ++ * @vid_range: CTAG VLAN ID range (used as mask when @vid_range_select is 0) ++ * @flex_field2_mask_range: Flexible field 2 mask or range ++ * @flex_field2_value: Flexible field 2 match value ++ * @port_id: Ingress port ID for classification ++ * @dscp: Outer DSCP value ++ * @inner_dscp: Inner DSCP value ++ * @pcp: CTAG VLAN PCP (bits 2:0) and DEI (bit 3) ++ * @stag_pcp_dei: STAG VLAN PCP (bits 2:0) and DEI (bit 3) ++ * @mac_dst: Destination MAC address ++ * @mac_src: Source MAC address ++ * @protocol: Outer IP protocol value ++ * @protocol_mask: Outer IP protocol nibble mask ++ * @inner_protocol: Inner IP protocol value ++ * @inner_protocol_mask: Inner IP protocol bit mask ++ * @flex_field4_parser_index: Flexible field 4 parser output index (0..127) ++ * @flex_field3_parser_index: Flexible field 3 parser output index (0..127) ++ * @flex_field1_parser_index: Flexible field 1 parser output index (0..127) ++ * @flex_field2_parser_index: Flexible field 2 parser output index (0..127) ++ * @enable: Rule is enabled (used) or disabled (unused) ++ * @port_id_enable: Enable ingress port ID matching ++ * @port_id_exclude: Exclude (negate) port ID match ++ * @sub_if_id_type: Sub-interface ID field mode selector ++ * @sub_if_id_enable: Enable sub-interface ID matching ++ * @sub_if_id_exclude: Exclude sub-interface ID match ++ * @dscp_enable: Enable outer DSCP matching ++ * @dscp_exclude: Exclude outer DSCP match ++ * @inner_dscp_enable: Enable inner DSCP matching ++ * @inner_dscp_exclude: Exclude inner DSCP match ++ * @pcp_enable: Enable CTAG PCP/DEI matching ++ * @ctag_pcp_dei_exclude: Exclude CTAG PCP/DEI match ++ * @stag_pcp_dei_enable: Enable STAG PCP/DEI matching ++ * @stag_pcp_dei_exclude: Exclude STAG PCP/DEI match ++ * @pkt_lng_enable: Enable packet length matching ++ * @pkt_lng_exclude: Exclude packet length match ++ * @mac_dst_enable: Enable destination MAC matching ++ * @dst_mac_exclude: Exclude destination MAC match ++ * @mac_src_enable: Enable source MAC matching ++ * @src_mac_exclude: Exclude source MAC match ++ * @app_data_msb_enable: Enable MSB application field matching ++ * @app_mask_range_msb_select: MSB application mask/range selection ++ * (0 = nibble mask, 1 = range) ++ * @app_msb_exclude: Exclude MSB application match ++ * @app_data_lsb_enable: Enable LSB application field matching ++ * @app_mask_range_lsb_select: LSB application mask/range selection ++ * (0 = nibble mask, 1 = range) ++ * @app_lsb_exclude: Exclude LSB application match ++ * @dst_ip_select: Outer destination IP selection ++ * (0 = disabled, 1 = IPv4, 2 = IPv6) ++ * @dst_ip: Outer destination IP address ++ * @dst_ip_exclude: Exclude outer destination IP match ++ * @inner_dst_ip_select: Inner destination IP selection ++ * @inner_dst_ip: Inner destination IP address ++ * @inner_dst_ip_exclude: Exclude inner destination IP match ++ * @src_ip_select: Outer source IP selection ++ * (0 = disabled, 1 = IPv4, 2 = IPv6) ++ * @src_ip: Outer source IP address ++ * @src_ip_exclude: Exclude outer source IP match ++ * @inner_src_ip_select: Inner source IP selection ++ * @inner_src_ip: Inner source IP address ++ * @inner_src_ip_exclude: Exclude inner source IP match ++ * @ether_type_enable: Enable EtherType matching ++ * @ether_type_exclude: Exclude EtherType match ++ * @protocol_enable: Enable outer IP protocol matching ++ * @protocol_exclude: Exclude outer IP protocol match ++ * @inner_protocol_enable: Enable inner IP protocol matching ++ * @inner_protocol_exclude: Exclude inner IP protocol match ++ * @session_id_enable: Enable PPPoE session ID matching ++ * @session_id_exclude: Exclude PPPoE session ID match ++ * @ppp_protocol_enable: Enable PPP protocol matching ++ * @ppp_protocol_exclude: Exclude PPP protocol match ++ * @vid_used: Enable CTAG VLAN ID matching ++ * @vid_range_select: CVLAN mask/range selection (0 = mask, 1 = range) ++ * @vid_exclude: Exclude CTAG VLAN ID match ++ * @vid_original: Use original VLAN ID as key even if modified earlier ++ * @slan_vid_used: Enable STAG VLAN ID matching ++ * @slan_vid_exclude: Exclude STAG VLAN ID match ++ * @svid_range_select: SVLAN mask/range selection (0 = mask, 1 = range) ++ * @outer_vid_original: Use original outer VLAN ID as key even if modified ++ * @payload1_src_enable: Enable payload-1 matching ++ * @payload1_mask_range_select: Payload-1 mask/range selection ++ * (0 = bit mask, 1 = range) ++ * @payload1_exclude: Exclude payload-1 match ++ * @payload2_src_enable: Enable payload-2 matching ++ * @payload2_mask_range_select: Payload-2 mask/range selection ++ * (0 = bit mask, 1 = range) ++ * @payload2_exclude: Exclude payload-2 match ++ * @parser_flag_lsb_enable: Enable parser flag LSW matching ++ * @parser_flag_lsb_exclude: Exclude parser flag LSW match ++ * @parser_flag_msb_enable: Enable parser flag MSW matching ++ * @parser_flag_msb_exclude: Exclude parser flag MSW match ++ * @parser_flag1_lsb_enable: Enable parser flag1 LSW matching ++ * @parser_flag1_lsb_exclude: Exclude parser flag1 LSW match ++ * @parser_flag1_msb_enable: Enable parser flag1 MSW matching ++ * @parser_flag1_msb_exclude: Exclude parser flag1 MSW match ++ * @insertion_flag_enable: Enable insertion flag matching ++ * @flex_field4_enable: Enable flexible field 4 matching ++ * @flex_field4_exclude_enable: Exclude flexible field 4 match ++ * @flex_field4_range_enable: Flexible field 4 range mode ++ * (0 = mask, 1 = range) ++ * @flex_field3_enable: Enable flexible field 3 matching ++ * @flex_field3_exclude_enable: Exclude flexible field 3 match ++ * @flex_field3_range_enable: Flexible field 3 range mode ++ * @flex_field2_enable: Enable flexible field 2 matching ++ * @flex_field2_exclude_enable: Exclude flexible field 2 match ++ * @flex_field2_range_enable: Flexible field 2 range mode ++ * @flex_field1_enable: Enable flexible field 1 matching ++ * @flex_field1_exclude_enable: Exclude flexible field 1 match ++ * @flex_field1_range_enable: Flexible field 1 range mode ++ */ ++struct mxl862xx_pce_pattern { ++ __le16 index; ++ __le32 dst_ip_mask; ++ __le32 inner_dst_ip_mask; ++ __le32 src_ip_mask; ++ __le32 inner_src_ip_mask; ++ __le16 sub_if_id; ++ __le16 pkt_lng; ++ __le16 pkt_lng_range; ++ __le16 mac_dst_mask; ++ __le16 mac_src_mask; ++ __le16 app_data_msb; ++ __le16 app_mask_range_msb; ++ __le16 ether_type; ++ __le16 ether_type_mask; ++ __le16 session_id; ++ __le16 ppp_protocol; ++ __le16 ppp_protocol_mask; ++ __le16 vid; ++ __le16 slan_vid; ++ __le16 flex_field4_value; ++ __le16 flex_field4_mask_range; ++ __le16 flex_field3_value; ++ __le16 flex_field3_mask_range; ++ __le16 flex_field1_value; ++ __le16 flex_field1_mask_range; ++ __le16 outer_vid_range; ++ __le16 payload1; ++ __le16 payload1_mask; ++ __le16 payload2; ++ __le16 payload2_mask; ++ __le16 parser_flag_lsb; ++ __le16 parser_flag_lsb_mask; ++ __le16 parser_flag_msb; ++ __le16 parser_flag_msb_mask; ++ __le16 parser_flag1_lsb; ++ __le16 parser_flag1_lsb_mask; ++ __le16 parser_flag1_msb; ++ __le16 parser_flag1_msb_mask; ++ __le16 app_data_lsb; ++ __le16 app_mask_range_lsb; ++ __le16 insertion_flag; ++ __le16 vid_range; ++ __le16 flex_field2_mask_range; ++ __le16 flex_field2_value; ++ u8 port_id; ++ u8 dscp; ++ u8 inner_dscp; ++ u8 pcp; ++ u8 stag_pcp_dei; ++ u8 mac_dst[ETH_ALEN]; ++ u8 mac_src[ETH_ALEN]; ++ u8 protocol; ++ u8 protocol_mask; ++ u8 inner_protocol; ++ u8 inner_protocol_mask; ++ u8 flex_field4_parser_index; ++ u8 flex_field3_parser_index; ++ u8 flex_field1_parser_index; ++ u8 flex_field2_parser_index; ++ u8 enable; ++ u8 port_id_enable; ++ u8 port_id_exclude; ++ __le32 sub_if_id_type; ++ u8 sub_if_id_enable; ++ u8 sub_if_id_exclude; ++ u8 dscp_enable; ++ u8 dscp_exclude; ++ u8 inner_dscp_enable; ++ u8 inner_dscp_exclude; ++ u8 pcp_enable; ++ u8 ctag_pcp_dei_exclude; ++ u8 stag_pcp_dei_enable; ++ u8 stag_pcp_dei_exclude; ++ u8 pkt_lng_enable; ++ u8 pkt_lng_exclude; ++ u8 mac_dst_enable; ++ u8 dst_mac_exclude; ++ u8 mac_src_enable; ++ u8 src_mac_exclude; ++ u8 app_data_msb_enable; ++ u8 app_mask_range_msb_select; ++ u8 app_msb_exclude; ++ u8 app_data_lsb_enable; ++ u8 app_mask_range_lsb_select; ++ u8 app_lsb_exclude; ++ __le32 dst_ip_select; ++ union mxl862xx_ip dst_ip; ++ u8 dst_ip_exclude; ++ __le32 inner_dst_ip_select; ++ union mxl862xx_ip inner_dst_ip; ++ u8 inner_dst_ip_exclude; ++ __le32 src_ip_select; ++ union mxl862xx_ip src_ip; ++ u8 src_ip_exclude; ++ __le32 inner_src_ip_select; ++ union mxl862xx_ip inner_src_ip; ++ u8 inner_src_ip_exclude; ++ u8 ether_type_enable; ++ u8 ether_type_exclude; ++ u8 protocol_enable; ++ u8 protocol_exclude; ++ u8 inner_protocol_enable; ++ u8 inner_protocol_exclude; ++ u8 session_id_enable; ++ u8 session_id_exclude; ++ u8 ppp_protocol_enable; ++ u8 ppp_protocol_exclude; ++ u8 vid_used; ++ u8 vid_range_select; ++ u8 vid_exclude; ++ u8 vid_original; ++ u8 slan_vid_used; ++ u8 slan_vid_exclude; ++ u8 svid_range_select; ++ u8 outer_vid_original; ++ u8 payload1_src_enable; ++ u8 payload1_mask_range_select; ++ u8 payload1_exclude; ++ u8 payload2_src_enable; ++ u8 payload2_mask_range_select; ++ u8 payload2_exclude; ++ u8 parser_flag_lsb_enable; ++ u8 parser_flag_lsb_exclude; ++ u8 parser_flag_msb_enable; ++ u8 parser_flag_msb_exclude; ++ u8 parser_flag1_lsb_enable; ++ u8 parser_flag1_lsb_exclude; ++ u8 parser_flag1_msb_enable; ++ u8 parser_flag1_msb_exclude; ++ u8 insertion_flag_enable; ++ u8 flex_field4_enable; ++ u8 flex_field4_exclude_enable; ++ u8 flex_field4_range_enable; ++ u8 flex_field3_enable; ++ u8 flex_field3_exclude_enable; ++ u8 flex_field3_range_enable; ++ u8 flex_field2_enable; ++ u8 flex_field2_exclude_enable; ++ u8 flex_field2_range_enable; ++ u8 flex_field1_enable; ++ u8 flex_field1_exclude_enable; ++ u8 flex_field1_range_enable; ++} __packed; ++ ++static_assert(sizeof(struct mxl862xx_pce_pattern) == 279); ++ ++/** ++ * struct mxl862xx_pce_action_pbb - Provider Backbone Bridging (Mac-in-Mac) ++ * action configuration ++ * ++ * @tunnel_id_known_traffic: Tunnel template index for I-Header known traffic ++ * @tunnel_id_unknown_traffic: Tunnel template index for I-Header unknown ++ * traffic ++ * @process_id_known_traffic: Tunnel template index for B-TAG known traffic ++ * @process_id_unknown_traffic: Tunnel template index for B-TAG unknown ++ * traffic ++ * @iheader_op_mode: I-Header operation mode (0 = no change, 1 = insert, ++ * 2 = remove, 3 = replace) ++ * @btag_op_mode: B-TAG operation mode (0 = no change, 1 = insert, ++ * 2 = remove, 3 = replace) ++ * @mac_table_macinmac_select: MAC table Mac-in-Mac selection ++ * (0 = outer MAC, 1 = inner MAC) ++ * @iheader_action_enable: Enable Mac-in-Mac I-Header action ++ * @tunnel_id_known_traffic_enable: Enable tunnel ID for known traffic ++ * @tunnel_id_unknown_traffic_enable: Enable tunnel ID for unknown traffic ++ * @b_dst_mac_from_mac_table_enable: Use B-DA from MAC table instead of ++ * tunnel template (I-Header insertion ++ * mode only) ++ * @replace_b_src_mac_enable: Replace B-SA from tunnel template ++ * @replace_b_dst_mac_enable: Replace B-DA from tunnel template ++ * @replace_i_tag_res_enable: Replace I-Tag Res from tunnel template ++ * @replace_i_tag_uac_enable: Replace I-Tag UAC from tunnel template ++ * @replace_i_tag_dei_enable: Replace I-Tag DEI from tunnel template ++ * @replace_i_tag_pcp_enable: Replace I-Tag PCP from tunnel template ++ * @replace_i_tag_sid_enable: Replace I-Tag SID from tunnel template ++ * @replace_i_tag_tpid_enable: Replace I-Tag TPID from tunnel template ++ * @btag_action_enable: Enable B-TAG action ++ * @process_id_known_traffic_enable: Enable process ID for B-TAG known ++ * traffic ++ * @process_id_unknown_traffic_enable: Enable process ID for B-TAG unknown ++ * traffic ++ * @replace_b_tag_dei_enable: Replace B-Tag DEI from tunnel template ++ * @replace_b_tag_pcp_enable: Replace B-Tag PCP from tunnel template ++ * @replace_b_tag_vid_enable: Replace B-Tag VID from tunnel template ++ * @replace_b_tag_tpid_enable: Replace B-Tag TPID from tunnel template ++ * @mac_table_macinmac_action_enable: Enable MAC table Mac-in-Mac action ++ */ ++struct mxl862xx_pce_action_pbb { ++ u8 tunnel_id_known_traffic; ++ u8 tunnel_id_unknown_traffic; ++ u8 process_id_known_traffic; ++ u8 process_id_unknown_traffic; ++ __le32 iheader_op_mode; ++ __le32 btag_op_mode; ++ __le32 mac_table_macinmac_select; ++ u8 iheader_action_enable; ++ u8 tunnel_id_known_traffic_enable; ++ u8 tunnel_id_unknown_traffic_enable; ++ u8 b_dst_mac_from_mac_table_enable; ++ u8 replace_b_src_mac_enable; ++ u8 replace_b_dst_mac_enable; ++ u8 replace_i_tag_res_enable; ++ u8 replace_i_tag_uac_enable; ++ u8 replace_i_tag_dei_enable; ++ u8 replace_i_tag_pcp_enable; ++ u8 replace_i_tag_sid_enable; ++ u8 replace_i_tag_tpid_enable; ++ u8 btag_action_enable; ++ u8 process_id_known_traffic_enable; ++ u8 process_id_unknown_traffic_enable; ++ u8 replace_b_tag_dei_enable; ++ u8 replace_b_tag_pcp_enable; ++ u8 replace_b_tag_vid_enable; ++ u8 replace_b_tag_tpid_enable; ++ u8 mac_table_macinmac_action_enable; ++} __packed; ++ ++static_assert(sizeof(struct mxl862xx_pce_action_pbb) == 36); ++ ++/** ++ * struct mxl862xx_pce_action_dest_subif - Destination sub-interface ID ++ * action configuration ++ * ++ * @dest_subifid_action_enable: Destination sub-interface ID group field ++ * action enable ++ * @dest_subifid_assignment_enable: Destination sub-interface ID group field ++ * assignment enable ++ * @dest_subifgrp_field: Destination sub-interface ID group field value, ++ * or LAG index when trunking action is enabled ++ */ ++struct mxl862xx_pce_action_dest_subif { ++ u8 dest_subifid_action_enable; ++ u8 dest_subifid_assignment_enable; ++ __le16 dest_subifgrp_field; ++} __packed; ++ ++static_assert(sizeof(struct mxl862xx_pce_action_dest_subif) == 4); ++ ++/** ++ * enum mxl862xx_pce_action_portmap - Forwarding group action selector ++ * ++ * Selects how the packet is forwarded. Mutually exclusive with ++ * the flow_id_action (bFlowID_Action in vendor API). ++ * ++ * @MXL862XX_PCE_ACTION_PORTMAP_DISABLE: Forwarding action is disabled ++ * @MXL862XX_PCE_ACTION_PORTMAP_REGULAR: Use default port-map from ++ * forwarding classification ++ * @MXL862XX_PCE_ACTION_PORTMAP_DISCARD: Discard the packet ++ * @MXL862XX_PCE_ACTION_PORTMAP_CPU: Forward to the CPU port ++ * (as configured by GSW_CPU_PortCfgSet, typically the on-die ++ * microcontroller -- not the DSA CPU port) ++ * @MXL862XX_PCE_ACTION_PORTMAP_ALTERNATIVE: Forward to an explicit ++ * portmap given by forward_port_map[] ++ */ ++enum mxl862xx_pce_action_portmap { ++ MXL862XX_PCE_ACTION_PORTMAP_DISABLE = 0, ++ MXL862XX_PCE_ACTION_PORTMAP_REGULAR = 1, ++ MXL862XX_PCE_ACTION_PORTMAP_DISCARD = 2, ++ MXL862XX_PCE_ACTION_PORTMAP_CPU = 3, ++ MXL862XX_PCE_ACTION_PORTMAP_ALTERNATIVE = 4, ++}; ++ ++/** ++ * enum mxl862xx_pce_action_cross_state - Cross state action selector ++ * ++ * Controls whether the packet ignores STP port-state filtering. ++ * ++ * @MXL862XX_PCE_ACTION_CROSS_STATE_DISABLE: Cross state action is disabled ++ * @MXL862XX_PCE_ACTION_CROSS_STATE_REGULAR: Enabled; packet is treated ++ * as non-cross-state (does not ignore port-state filtering) ++ * @MXL862XX_PCE_ACTION_CROSS_STATE_CROSS: Enabled; packet ignores ++ * port-state filtering rules (e.g. passes through BLOCKING state) ++ */ ++enum mxl862xx_pce_action_cross_state { ++ MXL862XX_PCE_ACTION_CROSS_STATE_DISABLE = 0, ++ MXL862XX_PCE_ACTION_CROSS_STATE_REGULAR = 1, ++ MXL862XX_PCE_ACTION_CROSS_STATE_CROSS = 2, ++}; ++ ++/** ++ * struct mxl862xx_pce_action - PCE rule action configuration ++ * ++ * Defines the actions applied to packets matching a PCE rule pattern. ++ * ++ * @time_comp: Signed time compensation value for OAM delay measurement ++ * @extended_vlan_block_id: Extended VLAN block allocated for this flow ++ * entry (valid when @extended_vlan_enable is set) ++ * @ins_ext_point: Insertion/extraction point ++ * @ptp_seq_id: PTP sequence ID for PTP application ++ * @pkt_update_offset: Byte offset (2..255) for counter/timestamp ++ * insertion (used when @no_pkt_update and ++ * @append_to_pkt are both false) ++ * @oam_flow_id: Traffic flow counter ID for OAM loss measurement ++ * @record_id: Record ID used by extraction and/or OAM process ++ * @forward_port_map: Target portmap for forwarded packets. Each bit ++ * represents one bridge port. Used when ++ * @port_map_action is %MXL862XX_PCE_ACTION_PORTMAP_ALTERNATIVE. ++ * @rmon_id: RMON counter ID (index starts from zero) ++ * @svlan_id: Alternative STAG VLAN ID ++ * @flow_id: Flow ID ++ * @rout_ext_id: Routing extension ID value (8-bit range) ++ * @traffic_class_alternate: Alternative traffic class (used when ++ * @traffic_class_action selects alternate) ++ * @meter_id: Meter ID ++ * @vlan_id: Alternative CTAG VLAN ID ++ * @fid: Alternative Filtering Identifier (FID) ++ * @traffic_class_action: Traffic class action selector ++ * (0 = disable, 1 = regular CoS, 2 = alternative) ++ * @snooping_type_action: IGMP snooping control selector ++ * @learning_action: MAC learning action selector ++ * (0 = disable, 1 = regular, 2 = force no learn, ++ * 3 = force learn) ++ * @irq_action: Interrupt action selector ++ * (0 = disable, 1 = regular, 2 = generate interrupt) ++ * @cross_state_action: Cross state action selector. ++ * See &enum mxl862xx_pce_action_cross_state ++ * @crit_frame_action: Critical frame action selector ++ * (0 = disable, 1 = regular, 2 = critical) ++ * @color_frame_action: Color frame action selector (replaces ++ * @crit_frame_action in GSWIP-3.1) ++ * @timestamp_action: Timestamp action selector ++ * (0 = disable, 1 = regular, 2 = store timestamps) ++ * @port_map_action: Forwarding portmap action selector. ++ * See &enum mxl862xx_pce_action_portmap ++ * @meter_action: Meter action selector ++ * (0 = disable, 1 = regular, 2 = meter 1, ++ * 3 = meter 1 and 2) ++ * @vlan_action: CTAG VLAN action selector ++ * (0 = disable, 1 = regular, 2 = alternative) ++ * @svlan_action: STAG VLAN action selector ++ * (0 = disable, 1 = regular, 2 = alternative) ++ * @vlan_cross_action: Cross-VLAN action selector ++ * (0 = disable, 1 = regular, 2 = cross-VLAN) ++ * @process_path_action: MPE processing path assignment ++ * (0 = unused, 1 = path-1, 2 = path-2, 3 = both) ++ * @port_filter_type_action: Port filter action type (0..6) ++ * @pbb_action: Provider Backbone Bridging (Mac-in-Mac) action. ++ * See &struct mxl862xx_pce_action_pbb ++ * @dest_subif_action: Destination sub-interface ID action. ++ * See &struct mxl862xx_pce_action_dest_subif ++ * @remark_action: Enable remarking action ++ * @remark_pcp: Enable CTAG VLAN PCP remarking ++ * @remark_stag_pcp: Enable STAG VLAN PCP remarking ++ * @remark_stag_dei: Enable STAG VLAN DEI remarking ++ * @remark_dscp: Enable DSCP remarking ++ * @remark_class: Enable class remarking ++ * @rmon_action: Enable RMON counter action ++ * @fid_enable: Enable alternative FID ++ * @extended_vlan_enable: Enable extended VLAN operation for matching ++ * traffic ++ * @cvlan_ignore_control: CVLAN ignore control ++ * @port_bitmap_mux_control: Port bitmap mux control ++ * @port_trunk_action: Enable trunking action ++ * @port_link_selection: Port link selection control ++ * @flow_id_action: Enable flow ID action (mutually exclusive with ++ * @port_map_action) ++ * @rout_ext_id_action: Enable routing extension ID action ++ * @rt_dst_port_mask_cmp_action: Routing destination port mask comparison ++ * @rt_src_port_mask_cmp_action: Routing source port mask comparison ++ * @rt_dst_ip_mask_cmp_action: Routing destination IP mask comparison ++ * @rt_src_ip_mask_cmp_action: Routing source IP mask comparison ++ * @rt_inner_ip_as_key_action: Use inner IP in tunneled header as ++ * routing key ++ * @rt_accel_ena_action: Routing acceleration enable ++ * @rt_ctrl_ena_action: Routing control enable (selects routing ++ * accelerate action) ++ * @extract_enable: Enable packet extraction at point defined by ++ * @record_id ++ * @oam_enable: Enable OAM processing for matching packets ++ * @pce_bypass_path: Update packet in PCE bypass path (after QoS queue) ++ * @tx_flow_cnt: Use TX flow counter (otherwise RX flow counter) ++ * @time_format: Timestamp format (0 = digital 10B, 1 = binary 10B, ++ * 2 = digital 8B, 3 = binary 8B) ++ * @no_pkt_update: Do not update packet ++ * @append_to_pkt: Append counter/timestamp to end of packet (when ++ * @no_pkt_update is false) ++ * @pbb_action_enable: Enable PBB action. See &struct mxl862xx_pce_action_pbb ++ * @dest_subif_action_enable: Enable destination sub-interface ID action. ++ * See &struct mxl862xx_pce_action_dest_subif ++ */ ++struct mxl862xx_pce_action { ++ __le64 time_comp; ++ __le16 extended_vlan_block_id; ++ u8 ins_ext_point; ++ u8 ptp_seq_id; ++ __le16 pkt_update_offset; ++ __le16 oam_flow_id; ++ __le16 record_id; ++ __le16 forward_port_map[8]; ++ __le16 rmon_id; ++ __le16 svlan_id; ++ __le16 flow_id; ++ __le16 rout_ext_id; ++ u8 traffic_class_alternate; ++ u8 meter_id; ++ u8 vlan_id; ++ u8 fid; ++ __le32 traffic_class_action; ++ __le32 snooping_type_action; ++ __le32 learning_action; ++ __le32 irq_action; ++ __le32 cross_state_action; ++ __le32 crit_frame_action; ++ __le32 color_frame_action; ++ __le32 timestamp_action; ++ __le32 port_map_action; ++ __le32 meter_action; ++ __le32 vlan_action; ++ __le32 svlan_action; ++ __le32 vlan_cross_action; ++ __le32 process_path_action; ++ __le32 port_filter_type_action; ++ struct mxl862xx_pce_action_pbb pbb_action; ++ struct mxl862xx_pce_action_dest_subif dest_subif_action; ++ u8 remark_action; ++ u8 remark_pcp; ++ u8 remark_stag_pcp; ++ u8 remark_stag_dei; ++ u8 remark_dscp; ++ u8 remark_class; ++ u8 rmon_action; ++ u8 fid_enable; ++ u8 extended_vlan_enable; ++ u8 cvlan_ignore_control; ++ u8 port_bitmap_mux_control; ++ u8 port_trunk_action; ++ u8 port_link_selection; ++ u8 flow_id_action; ++ u8 rout_ext_id_action; ++ u8 rt_dst_port_mask_cmp_action; ++ u8 rt_src_port_mask_cmp_action; ++ u8 rt_dst_ip_mask_cmp_action; ++ u8 rt_src_ip_mask_cmp_action; ++ u8 rt_inner_ip_as_key_action; ++ u8 rt_accel_ena_action; ++ u8 rt_ctrl_ena_action; ++ u8 extract_enable; ++ u8 oam_enable; ++ u8 pce_bypass_path; ++ u8 tx_flow_cnt; ++ __le32 time_format; ++ u8 no_pkt_update; ++ u8 append_to_pkt; ++ u8 pbb_action_enable; ++ u8 dest_subif_action_enable; ++} __packed; ++ ++static_assert(sizeof(struct mxl862xx_pce_action) == 180); ++ ++/** ++ * enum mxl862xx_pce_rule_region - PCE rule table region selector ++ * ++ * Selects which region of the traffic flow table the rule belongs to. ++ * ++ * @MXL862XX_PCE_RULE_COMMON: Common region shared by all CTPs ++ * (global rules, indices 0..63) ++ * @MXL862XX_PCE_RULE_CTP: Per-CTP region. The rule index is relative ++ * to the CTP block identified by logicalportid; the firmware ++ * translates it to an absolute hardware index. ++ * @MXL862XX_PCE_RULE_DEBUG: Debug region with direct HW index mapping ++ */ ++enum mxl862xx_pce_rule_region { ++ MXL862XX_PCE_RULE_COMMON = 0, ++ MXL862XX_PCE_RULE_CTP = 1, ++ MXL862XX_PCE_RULE_DEBUG = 2, ++}; ++ ++/** ++ * struct mxl862xx_pce_rule - PCE rule configuration ++ * @logicalportid: Logical Port Id ++ * @subifidgroup: Sub-interface ID group ++ * @region: PCE rule region (common or per-CTP) ++ * @pattern: Match pattern (destination MAC, EtherType, etc.) ++ * @action: Forwarding action (portmap, cross-state, etc.) ++ * ++ * This structure is passed to the firmware via the MDIO data ++ * buffer using the %MXL862XX_TFLOW_PCERULEWRITE command. ++ * The binary layout must exactly match the firmware's ++ * GSW_PCE_rule_t (466 bytes, packed, little-endian scalars). ++ */ ++struct mxl862xx_pce_rule { ++ u8 logicalportid; ++ __le16 subifidgroup; ++ __le32 region; ++ struct mxl862xx_pce_pattern pattern; ++ struct mxl862xx_pce_action action; ++} __packed; ++ ++static_assert(sizeof(struct mxl862xx_pce_rule) == 466); ++ ++/** ++ * struct mxl862xx_pce_rule_alloc - PCE rule block allocation ++ * @num_of_rules: Number of rules to allocate (input) / allocated (output). ++ * The firmware rounds up to a multiple of four consecutive entries. ++ * @blockid: Starting rule index of the allocated block (output on alloc, ++ * input on free). ++ * ++ * Used with %MXL862XX_TFLOW_PCERULEALLOC and %MXL862XX_TFLOW_PCERULEFREE. ++ * Maps to the firmware's ``GSW_PCE_rule_alloc_t``. ++ */ ++struct mxl862xx_pce_rule_alloc { ++ __le16 num_of_rules; ++ __le16 blockid; ++} __packed; ++ ++static_assert(sizeof(struct mxl862xx_pce_rule_alloc) == 4); ++ + /** + * enum mxl862xx_stp_port_state - Spanning Tree Protocol port states + * @MXL862XX_STP_PORT_STATE_FORWARD: Forwarding state +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -12,6 +12,7 @@ + (MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1) + + #define MXL862XX_COMMON_MAGIC 0x100 ++#define MXL862XX_TFLOW_MAGIC 0x200 + #define MXL862XX_BRDG_MAGIC 0x300 + #define MXL862XX_BRDGPORT_MAGIC 0x400 + #define MXL862XX_CTP_MAGIC 0x500 +@@ -31,6 +32,10 @@ + #define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa) + #define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11) + ++#define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2) ++#define MXL862XX_TFLOW_PCERULEALLOC (MXL862XX_TFLOW_MAGIC + 0x4) ++#define MXL862XX_TFLOW_PCERULEFREE (MXL862XX_TFLOW_MAGIC + 0x5) ++ + #define MXL862XX_BRIDGE_ALLOC (MXL862XX_BRDG_MAGIC + 0x1) + #define MXL862XX_BRIDGE_CONFIGSET (MXL862XX_BRDG_MAGIC + 0x2) + #define MXL862XX_BRIDGE_CONFIGGET (MXL862XX_BRDG_MAGIC + 0x3) +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -280,9 +280,11 @@ static int mxl862xx_wait_ready(struct ds + ver.iv_major, ver.iv_minor, + le16_to_cpu(ver.iv_revision), + le32_to_cpu(ver.iv_build_num)); ++ + priv->fw_version.major = ver.iv_major; + priv->fw_version.minor = ver.iv_minor; + priv->fw_version.revision = le16_to_cpu(ver.iv_revision); ++ + return 0; + + not_ready_yet: +@@ -410,6 +412,68 @@ static int mxl862xx_setup_drop_meter(str + return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg); + } + ++ ++/* Per-CTP offset used for the link-local trap rule. Each port's CTP ++ * flow-table block is pre-allocated by the firmware during init (44 ++ * entries per port on a 10-port SKU, of which offset 0 is reserved ++ * for flow-control marking). Offset 1 is the first unused slot. ++ */ ++#define MXL862XX_LINK_LOCAL_CTP_OFFSET 1 ++ ++/* Install a PCE rule that traps IEEE 802.1D link-local frames ++ * (01:80:c2:00:00:0x) to the CPU port for a single user port, ++ * preventing the hardware bridge from flooding them to other ports. ++ * The firmware does not install this rule by default because its own ++ * STP module is not used when DSA manages STP. ++ * ++ * The rule is written into the port's per-CTP flow table at offset 1. ++ * The firmware already allocates a 44-entry block for every CTP during ++ * init (8 entries exposed initially, expandable), so no dynamic ++ * allocation via PCERULEALLOC is needed. Using region=CTP causes the ++ * firmware to translate the CTP-relative offset into an absolute ++ * hardware index. ++ * ++ * Cross-state is enabled so that link-local frames reach the CPU even ++ * when the bridge port is in BLOCKING or LEARNING state. ++ */ ++static int mxl862xx_setup_link_local_trap(struct dsa_switch *ds, int port) ++{ ++ DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS); ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct mxl862xx_pce_rule rule = {}; ++ int cpu_port = dp->cpu_dp->index; ++ int i; ++ ++ /* Address this port's CTP flow-table block */ ++ rule.logicalportid = port; ++ rule.subifidgroup = 0; ++ rule.region = cpu_to_le32(MXL862XX_PCE_RULE_CTP); ++ ++ /* Pattern: link-local MAC on this specific ingress port */ ++ rule.pattern.index = cpu_to_le16(MXL862XX_LINK_LOCAL_CTP_OFFSET); ++ rule.pattern.enable = 1; ++ rule.pattern.mac_dst_enable = 1; ++ memcpy(rule.pattern.mac_dst, eth_reserved_addr_base, ETH_ALEN); ++ rule.pattern.mac_dst_mask = cpu_to_le16(0x0001); ++ ++ /* Action: forward to the CPU port via explicit portmap */ ++ rule.action.port_map_action = ++ cpu_to_le32(MXL862XX_PCE_ACTION_PORTMAP_ALTERNATIVE); ++ ++ bitmap_zero(portmap, MXL862XX_MAX_BRIDGE_PORTS); ++ __set_bit(cpu_port, portmap); ++ for (i = 0; i < ARRAY_SIZE(rule.action.forward_port_map); i++) ++ rule.action.forward_port_map[i] = ++ cpu_to_le16(bitmap_read(portmap, i * 16, 16)); ++ ++ /* Bypass STP port state */ ++ rule.action.cross_state_action = ++ cpu_to_le32(MXL862XX_PCE_ACTION_CROSS_STATE_CROSS); ++ ++ return MXL862XX_API_WRITE(ds->priv, MXL862XX_TFLOW_PCERULEWRITE, ++ rule); ++} ++ + static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) + { + struct mxl862xx_bridge_port_config br_port_cfg = {}; +@@ -1594,6 +1658,11 @@ static int mxl862xx_port_setup(struct ds + if (ret) + return ret; + ++ /* install link-local trap for this user port */ ++ ret = mxl862xx_setup_link_local_trap(ds, port); ++ if (ret) ++ return ret; ++ + /* Initialize and pre-allocate per-port EVLAN and VF blocks for + * user ports. CPU ports do not use EVLAN or VF -- frames pass + * through without processing. Pre-allocation avoids firmware diff --git a/target/linux/generic/pending-6.12/760-15-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch b/target/linux/generic/pending-6.12/760-15-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch new file mode 100644 index 00000000000..38dc45c594f --- /dev/null +++ b/target/linux/generic/pending-6.12/760-15-net-dsa-mxl862xx-warn-about-old-firmware-default-PCE.patch @@ -0,0 +1,32 @@ +From 3bba25f7ba35e3bca8230bd37ffb612944dbf301 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 18:51:21 +0000 +Subject: [PATCH 24/35] net: dsa: mxl862xx: warn about old firmware default PCE + rules + +Firmware versions older than 1.0.80 install global PCE rules at +boot that redirect link-local frames (BPDUs, LLDP, LACP) to port 0 +(the on-chip microcontroller) instead of the DSA CPU port. With +port 0 disabled under DSA, these rules silently drop matching +traffic. + +Log a warning when old firmware is detected so users know to update. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -854,6 +854,10 @@ static int mxl862xx_setup(struct dsa_swi + if (ret) + return ret; + ++ if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80)) ++ dev_warn(ds->dev, "firmware < 1.0.80 installs global PCE rules " ++ "that interfere with DSA operation, please update\n"); ++ + schedule_delayed_work(&priv->stats_work, + MXL862XX_STATS_POLL_INTERVAL); + diff --git a/target/linux/generic/pending-6.12/760-16-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch b/target/linux/generic/pending-6.12/760-16-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch new file mode 100644 index 00000000000..86638830775 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-16-net-dsa-add-802.1Q-VLAN-based-tag-driver-for-MxL862x.patch @@ -0,0 +1,2466 @@ +From 1687c5632dfd80461b12425b943e30555faa3dd4 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 22 Mar 2026 00:58:04 +0000 +Subject: [PATCH 25/35] net: dsa: add 802.1Q VLAN-based tag driver for MxL862xx + +The MxL862xx native 8-byte special tag (SpTag) requires firmware +support on the switch CPU and is not compatible with all SoC Ethernet +controllers. An 802.1Q VLAN-based tagging alternative allows the +switch to operate with any standard Ethernet MAC that supports VLAN +tag insertion and stripping. + +Add a DSA tag driver that uses the tag_8021q framework to encode +source and destination port information in standard 802.1Q VLAN +tags. Register the DSA_TAG_PROTO_MXL862_8021Q protocol in the DSA +header. Map TX queue priority to PCP in the xmit path and extract +switch_id and source port from the management VID in the receive +path. Set promisc_on_conduit so the conduit interface accepts +frames with management VIDs that are not in its own VLAN filter. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/Kconfig | 1 + + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 221 +++ + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 2 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 1626 ++++++++++++++++++++--- + drivers/net/dsa/mxl862xx/mxl862xx.h | 21 +- + include/net/dsa.h | 2 + + net/dsa/Kconfig | 7 + + net/dsa/Makefile | 1 + + net/dsa/tag_mxl862xx_8021q.c | 59 + + 9 files changed, 1738 insertions(+), 202 deletions(-) + create mode 100644 net/dsa/tag_mxl862xx_8021q.c + +--- a/drivers/net/dsa/mxl862xx/Kconfig ++++ b/drivers/net/dsa/mxl862xx/Kconfig +@@ -4,6 +4,7 @@ config NET_DSA_MXL862 + depends on NET_DSA + select CRC16 + select NET_DSA_TAG_MXL_862XX ++ select NET_DSA_TAG_MXL_862XX_8021Q + help + This enables support for the MaxLinear MxL862xx switch family. + These switches have two 10GE SerDes interfaces, one typically +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -1185,6 +1185,227 @@ struct mxl862xx_ctp_port_assignment { + } __packed; + + /** ++ * enum mxl862xx_ctp_port_config_mask - CTP Port Configuration Mask ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_BRIDGE_PORT_ID: ++ * Mask for bridge_port_id. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_FORCE_TRAFFIC_CLASS: ++ * Mask for forced_traffic_class and default_traffic_class. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN: ++ * Mask for ingress_extended_vlan_enable and ++ * ingress_extended_vlan_block_id. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN_IGMP: ++ * Mask for ingress_extended_vlan_igmp_enable and ++ * ingress_extended_vlan_block_id_igmp. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN: ++ * Mask for egress_extended_vlan_enable and ++ * egress_extended_vlan_block_id. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN_IGMP: ++ * Mask for egress_extended_vlan_igmp_enable and ++ * egress_extended_vlan_block_id_igmp. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_NTO1_VLAN: ++ * Mask for ingress_nto1vlan_enable. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_NTO1_VLAN: ++ * Mask for egress_nto1vlan_enable. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_MARKING: ++ * Mask for ingress_marking_mode. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_MARKING: ++ * Mask for egress_marking_mode. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_MARKING_OVERRIDE: ++ * Mask for egress_marking_override_enable and ++ * egress_marking_mode_override. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_REMARKING: ++ * Mask for egress_remarking_mode. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_METER: ++ * Mask for ingress_metering_enable and ingress_traffic_meter_id. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_METER: ++ * Mask for egress_metering_enable and egress_traffic_meter_id. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_BRIDGING_BYPASS: ++ * Mask for bridging_bypass, dest_logical_port_id, pmapper_enable, ++ * dest_sub_if_id_group, pmapper_mapping_mode, pmapper_id_valid and ++ * pmapper_dest_sub_if_id_group. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_FLOW_ENTRY: ++ * Mask for first_flow_entry_index and number_of_flow_entries. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_LOOPBACK_AND_MIRROR: ++ * Mask for ingress_loopback_enable, ingress_da_sa_swap_enable, ++ * egress_loopback_enable, egress_da_sa_swap_enable, ++ * ingress_mirror_enable and egress_mirror_enable. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_ALL: Enable all fields. ++ * @MXL862XX_CTP_PORT_CONFIG_MASK_FORCE: Bypass any check for debug purpose. ++ */ ++enum mxl862xx_ctp_port_config_mask { ++ MXL862XX_CTP_PORT_CONFIG_MASK_BRIDGE_PORT_ID = BIT(0), ++ MXL862XX_CTP_PORT_CONFIG_MASK_FORCE_TRAFFIC_CLASS = BIT(1), ++ MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN = BIT(2), ++ MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN_IGMP = BIT(3), ++ MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN = BIT(4), ++ MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN_IGMP = BIT(5), ++ MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_NTO1_VLAN = BIT(6), ++ MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_NTO1_VLAN = BIT(7), ++ MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_MARKING = BIT(8), ++ MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_MARKING = BIT(9), ++ MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_MARKING_OVERRIDE = BIT(10), ++ MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_REMARKING = BIT(11), ++ MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_METER = BIT(12), ++ MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_METER = BIT(13), ++ MXL862XX_CTP_PORT_CONFIG_MASK_BRIDGING_BYPASS = BIT(14), ++ MXL862XX_CTP_PORT_CONFIG_MASK_FLOW_ENTRY = BIT(15), ++ MXL862XX_CTP_PORT_CONFIG_MASK_LOOPBACK_AND_MIRROR = BIT(16), ++ MXL862XX_CTP_PORT_CONFIG_MASK_ALL = 0x7FFFFFFF, ++ MXL862XX_CTP_PORT_CONFIG_MASK_FORCE = BIT(31), ++}; ++ ++/** ++ * struct mxl862xx_ctp_port_config - CTP Port Configuration ++ * @logical_port_id: Logical Port Id. The valid range is hardware dependent. ++ * Ignored when mask has ++ * %MXL862XX_CTP_PORT_CONFIG_MASK_FORCE. ++ * @n_sub_if_id_group: Sub interface ID group. The valid range is ++ * hardware/protocol dependent. When mask has ++ * %MXL862XX_CTP_PORT_CONFIG_MASK_FORCE, this is the ++ * absolute CTP index in hardware (debug only). ++ * @mask: See &enum mxl862xx_ctp_port_config_mask ++ * @bridge_port_id: Ingress Bridge Port ID to which this CTP port is ++ * associated for ingress traffic ++ * @forced_traffic_class: Default traffic class cannot be overridden by other ++ * rules (except traffic flow table and special tag) ++ * @default_traffic_class: Default traffic class for all ingress traffic from ++ * this CTP port ++ * @ingress_extended_vlan_enable: Enable extended VLAN processing for ingress ++ * non-IGMP traffic ++ * @ingress_extended_vlan_block_id: Extended VLAN block allocated for ingress ++ * non-IGMP traffic. Valid when ++ * ingress_extended_vlan_enable is set. ++ * @ingress_extended_vlan_block_size: Extended VLAN block size for ingress ++ * non-IGMP traffic. If 0, the block size of ++ * ingress_extended_vlan_block_id is used. ++ * @ingress_extended_vlan_igmp_enable: Enable extended VLAN processing for ++ * ingress IGMP traffic ++ * @ingress_extended_vlan_block_id_igmp: Extended VLAN block allocated for ++ * ingress IGMP traffic. Valid when ++ * ingress_extended_vlan_igmp_enable is ++ * set. ++ * @ingress_extended_vlan_block_size_igmp: Extended VLAN block size for ingress ++ * IGMP traffic. If 0, the block size of ++ * ingress_extended_vlan_block_id_igmp ++ * is used. ++ * @egress_extended_vlan_enable: Enable extended VLAN processing for egress ++ * non-IGMP traffic ++ * @egress_extended_vlan_block_id: Extended VLAN block allocated for egress ++ * non-IGMP traffic. Valid when ++ * egress_extended_vlan_enable is set. ++ * @egress_extended_vlan_block_size: Extended VLAN block size for egress ++ * non-IGMP traffic. If 0, the block size of ++ * egress_extended_vlan_block_id is used. ++ * @egress_extended_vlan_igmp_enable: Enable extended VLAN processing for ++ * egress IGMP traffic ++ * @egress_extended_vlan_block_id_igmp: Extended VLAN block allocated for ++ * egress IGMP traffic. Valid when ++ * egress_extended_vlan_igmp_enable is set. ++ * @egress_extended_vlan_block_size_igmp: Extended VLAN block size for egress ++ * IGMP traffic. If 0, the block size of ++ * egress_extended_vlan_block_id_igmp is ++ * used. ++ * @ingress_nto1vlan_enable: If enabled and ingress packet is VLAN tagged, ++ * outer VLAN ID is used for nSubIfId field in MAC ++ * table; otherwise 0 is used ++ * @egress_nto1vlan_enable: If enabled and egress packet is known unicast, ++ * outer VLAN ID is from nSubIfId field in MAC table ++ * @ingress_marking_mode: Ingress color marking mode for ingress traffic ++ * @egress_marking_mode: Egress color marking mode for ingress traffic at ++ * egress priority queue color marking stage ++ * @egress_marking_override_enable: Override color marking mode from last stage ++ * @egress_marking_mode_override: Egress color marking mode for egress traffic. ++ * Valid only when ++ * egress_marking_override_enable is set. ++ * @egress_remarking_mode: Color remarking for egress traffic ++ * @ingress_metering_enable: Traffic metering on ingress traffic applies ++ * @ingress_traffic_meter_id: Meter for ingress CTP process ++ * @egress_metering_enable: Traffic metering on egress traffic applies ++ * @egress_traffic_meter_id: Meter for egress CTP process ++ * @bridging_bypass: Ingress traffic bypasses bridging/multicast processing. ++ * Traffic flow table is not bypassed. ++ * @dest_logical_port_id: Destination logical port when bridging_bypass is set ++ * @pmapper_enable: When bridging_bypass is set, selects whether to use ++ * dest_sub_if_id_group or P-mapper for sub interface ++ * @dest_sub_if_id_group: Destination sub interface ID group when ++ * bridging_bypass is set and pmapper_enable is false ++ * @pmapper_mapping_mode: When bridging_bypass and pmapper_enable are set, ++ * selects DSCP or PCP to derive sub interface ID ++ * @pmapper_id_valid: When set, P-mapper is re-used and no new allocation or ++ * value change occurs. When false, allocation is handled ++ * by the API. ++ * @pmapper_id: P-mapper ID. Valid when pmapper_id_valid is set. ++ * @pmapper_dest_sub_if_id_group: P-mapper destination sub interface ID group ++ * entries (73 bytes, firmware layout) ++ * @first_flow_entry_index: First traffic flow table entry associated to this ++ * CTP port. Should be a multiple of 4. ++ * @number_of_flow_entries: Number of traffic flow table entries associated to ++ * this CTP port. Should be a multiple of 4. ++ * @ingress_loopback_enable: Ingress traffic is redirected to ingress logical ++ * port of this CTP with source sub interface ID as ++ * destination. Bypasses processing except flow table. ++ * @ingress_da_sa_swap_enable: Destination/Source MAC address of ingress traffic ++ * is swapped before transmitted ++ * @egress_loopback_enable: Egress traffic to this CTP port is redirected to ++ * ingress logical port with same sub interface ID ++ * @egress_da_sa_swap_enable: Destination/Source MAC address of egress traffic ++ * is swapped before transmitted ++ * @ingress_mirror_enable: If enabled, ingress traffic is mirrored to the ++ * monitoring port. Mutually exclusive with ++ * ingress_loopback_enable. ++ * @egress_mirror_enable: If enabled, egress traffic is mirrored to the ++ * monitoring port. Mutually exclusive with ++ * egress_loopback_enable. ++ */ ++struct mxl862xx_ctp_port_config { ++ u8 logical_port_id; ++ __le16 n_sub_if_id_group; ++ __le32 mask; ++ __le16 bridge_port_id; ++ u8 forced_traffic_class; ++ u8 default_traffic_class; ++ u8 ingress_extended_vlan_enable; ++ __le16 ingress_extended_vlan_block_id; ++ __le16 ingress_extended_vlan_block_size; ++ u8 ingress_extended_vlan_igmp_enable; ++ __le16 ingress_extended_vlan_block_id_igmp; ++ __le16 ingress_extended_vlan_block_size_igmp; ++ u8 egress_extended_vlan_enable; ++ __le16 egress_extended_vlan_block_id; ++ __le16 egress_extended_vlan_block_size; ++ u8 egress_extended_vlan_igmp_enable; ++ __le16 egress_extended_vlan_block_id_igmp; ++ __le16 egress_extended_vlan_block_size_igmp; ++ u8 ingress_nto1vlan_enable; ++ u8 egress_nto1vlan_enable; ++ __le32 ingress_marking_mode; ++ __le32 egress_marking_mode; ++ u8 egress_marking_override_enable; ++ __le32 egress_marking_mode_override; ++ __le32 egress_remarking_mode; ++ u8 ingress_metering_enable; ++ __le16 ingress_traffic_meter_id; ++ u8 egress_metering_enable; ++ __le16 egress_traffic_meter_id; ++ u8 bridging_bypass; ++ u8 dest_logical_port_id; ++ u8 pmapper_enable; ++ __le16 dest_sub_if_id_group; ++ __le32 pmapper_mapping_mode; ++ u8 pmapper_id_valid; ++ __le16 pmapper_id; ++ u8 pmapper_dest_sub_if_id_group[73]; ++ __le16 first_flow_entry_index; ++ __le16 number_of_flow_entries; ++ u8 ingress_loopback_enable; ++ u8 ingress_da_sa_swap_enable; ++ u8 egress_loopback_enable; ++ u8 egress_da_sa_swap_enable; ++ u8 ingress_mirror_enable; ++ u8 egress_mirror_enable; ++} __packed; ++ ++/** + * enum mxl862xx_port_duplex - Ethernet port duplex status + * @MXL862XX_DUPLEX_FULL: Port operates in full-duplex mode + * @MXL862XX_DUPLEX_HALF: Port operates in half-duplex mode +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -47,12 +47,14 @@ + #define MXL862XX_BRIDGEPORT_FREE (MXL862XX_BRDGPORT_MAGIC + 0x4) + + #define MXL862XX_CTP_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3) ++#define MXL862XX_CTP_PORTCONFIGSET (MXL862XX_CTP_MAGIC + 0x5) + + #define MXL862XX_QOS_METERCFGSET (MXL862XX_QOS_MAGIC + 0x2) + #define MXL862XX_QOS_METERALLOC (MXL862XX_QOS_MAGIC + 0x2a) + + #define MXL862XX_RMON_PORT_GET (MXL862XX_RMON_MAGIC + 0x1) + ++#define MXL862XX_MAC_TABLECLEAR (MXL862XX_SWMAC_MAGIC + 0x1) + #define MXL862XX_MAC_TABLEENTRYADD (MXL862XX_SWMAC_MAGIC + 0x2) + #define MXL862XX_MAC_TABLEENTRYREAD (MXL862XX_SWMAC_MAGIC + 0x3) + #define MXL862XX_MAC_TABLEENTRYQUERY (MXL862XX_SWMAC_MAGIC + 0x4) +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + + #include "mxl862xx.h" +@@ -115,6 +116,9 @@ enum mxl862xx_evlan_action { + EVLAN_PVID_OR_DISCARD, /* insert PVID tag or discard if no PVID */ + EVLAN_PVID_OR_PASS, /* insert PVID tag or pass-through */ + EVLAN_STRIP1_AND_PVID_OR_DISCARD,/* strip 1 tag + insert PVID, or discard */ ++ EVLAN_INSERT_OUTER, /* insert outer tag with mgmt_vid */ ++ EVLAN_STRIP1, /* strip 1 tag unconditionally */ ++ EVLAN_REASSIGN, /* reassign bridge port (keep tags) */ + }; + + struct mxl862xx_evlan_rule_desc { +@@ -124,6 +128,7 @@ struct mxl862xx_evlan_rule_desc { + u8 inner_tpid; /* enum mxl862xx_extended_vlan_filter_tpid */ + bool match_vid; /* true: match on VID from the vid parameter */ + u8 action; /* enum mxl862xx_evlan_action */ ++ u16 bridge_port_id; /* for EVLAN_REASSIGN */ + }; + + /* Shorthand constants for readability */ +@@ -190,11 +195,69 @@ static const struct mxl862xx_evlan_rule_ + { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, true, EVLAN_STRIP_IF_UNTAGGED }, + }; + ++/* ++ * tag_8021q: virtual bridge port egress rules. ++ * ++ * Inserts the management VID as an outer 802.1Q tag on all frames ++ * exiting toward the CPU via a virtual bridge port. Covers every ++ * possible frame type (untagged, single-tagged, double-tagged). ++ * ++ * 802.1Q ACCEPT rules must precede NO_FILTER catchalls to prevent ++ * NO_FILTER from matching standard 802.1Q frames first. ++ */ ++static const struct mxl862xx_evlan_rule_desc cpu_egress_tag_8021q[] = { ++ /* 802.1Q outer + inner present */ ++ { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, false, EVLAN_INSERT_OUTER }, ++ /* 802.1Q outer, no inner */ ++ { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, false, EVLAN_INSERT_OUTER }, ++ /* Non-8021Q outer + inner present */ ++ { FT_NO_FILTER, FT_NO_FILTER, TP_NONE, TP_NONE, false, EVLAN_INSERT_OUTER }, ++ /* Non-8021Q outer only */ ++ { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_INSERT_OUTER }, ++ /* Untagged */ ++ { FT_NO_TAG, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_INSERT_OUTER }, ++}; ++ ++/* ++ * tag_8021q: CPU port ingress reassignment rules. ++ * ++ * Each user port with a management VID gets these rules on the CPU port's ++ * ingress EVLAN block. They match the management VID as outer 802.1Q tag ++ * and reassign the frame to the user port's virtual bridge port. ++ * ++ * NO_FILTER is used for the inner position so that frames with any inner ++ * TPID (including non-802.1Q TPIDs like 802.1ad 0x88A8) are routed ++ * correctly. The management VID tag is kept and stripped later by the ++ * user port's egress EVLAN catchall rules. ++ * ++ * The bridge_port_id is overridden per-port at programming time. ++ */ ++static const struct mxl862xx_evlan_rule_desc cpu_ingress_reassign[] = { ++ /* Mgmt VID outer + any inner tag present */ ++ { FT_NORMAL, FT_NO_FILTER, TP_8021Q, TP_NONE, true, EVLAN_REASSIGN }, ++ /* Mgmt VID outer, no inner */ ++ { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, true, EVLAN_REASSIGN }, ++}; ++ ++/* User port egress catchall rules for tag_8021q mode. ++ * Strip the outer management VID tag from CPU->user frames that were ++ * not matched by any per-VID egress rule. Appended to the user port ++ * egress EVLAN block when tag_8021q is active. ++ */ ++static const struct mxl862xx_evlan_rule_desc tag_8021q_egress_strip[] = { ++ /* Any outer tag + inner present: strip outer (mgmt VID) */ ++ { FT_NO_FILTER, FT_NO_FILTER, TP_NONE, TP_NONE, false, EVLAN_STRIP1 }, ++ /* Any outer tag, no inner: strip it */ ++ { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_STRIP1 }, ++}; ++ + static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol m) + { +- return DSA_TAG_PROTO_MXL862; ++ struct mxl862xx_priv *priv = ds->priv; ++ ++ return priv->tag_proto; + } + + /* PHY access via firmware relay */ +@@ -420,6 +483,78 @@ static int mxl862xx_setup_drop_meter(str + */ + #define MXL862XX_LINK_LOCAL_CTP_OFFSET 1 + ++/** ++ * mxl862xx_cpu_bridge_port_id - Get the bridge port ID for CPU-side forwarding ++ * @ds: DSA switch ++ * @port: user port number ++ * ++ * In tag_8021q mode, returns the virtual bridge port ID so that frames ++ * destined for the CPU pass through the virtual bridge port's egress ++ * EVLAN (which inserts the management VID). In native SpTag mode, ++ * returns the physical CPU port index. ++ */ ++static int mxl862xx_cpu_bridge_port_id(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && p->bridge_port_cpu) ++ return p->bridge_port_cpu; ++ ++ return dsa_to_port(ds, port)->cpu_dp->index; ++} ++ ++/** ++ * mxl862xx_tag_8021q_disable_cpu_egress - Disable virtual bridge port egress EVLAN ++ * @ds: DSA switch ++ * @port: user port whose virtual bridge port egress EVLAN to disable ++ */ ++static void mxl862xx_tag_8021q_disable_cpu_egress(struct dsa_switch *ds, ++ int port) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ struct mxl862xx_bridge_port_config bp_cfg = {}; ++ ++ if (!p->bridge_port_cpu || !p->cpu_egress_evlan.allocated) ++ return; ++ ++ /* Disable egress EVLAN on the virtual bridge port */ ++ bp_cfg.bridge_port_id = cpu_to_le16(p->bridge_port_cpu); ++ bp_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN); ++ bp_cfg.egress_extended_vlan_enable = 0; ++ MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, bp_cfg); ++ ++ p->cpu_egress_evlan.in_use = false; ++} ++ ++/** ++ * mxl862xx_set_cpu_ctp_ingress_evlan - Assign ingress EVLAN to the CPU ++ * port's CTP ++ * @ds: DSA switch ++ * @cpu: CPU port index ++ * ++ * Both the reference and legacy drivers assign the CPU port's ingress ++ * EVLAN at the CTP level (via CTP_PORTCONFIGSET) rather than the ++ * bridge port level (BRIDGEPORT_CONFIGSET). The GSWIP ingress ++ * pipeline evaluates Bridge Port EVLAN first, then CTP EVLAN; the ++ * bridge port reassignment treatment used by tag_8021q only works ++ * reliably from the CTP level. ++ */ ++static int mxl862xx_set_cpu_ctp_ingress_evlan(struct dsa_switch *ds, int cpu) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_evlan_block *blk = &priv->ports[cpu].ingress_evlan; ++ struct mxl862xx_ctp_port_config ctp = {}; ++ ++ ctp.logical_port_id = cpu; ++ ctp.mask = cpu_to_le32(MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN); ++ ctp.ingress_extended_vlan_enable = blk->in_use; ++ ctp.ingress_extended_vlan_block_id = cpu_to_le16(blk->block_id); ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_CTP_PORTCONFIGSET, ctp); ++} ++ + /* Install a PCE rule that traps IEEE 802.1D link-local frames + * (01:80:c2:00:00:0x) to the CPU port for a single user port, + * preventing the hardware bridge from flooding them to other ports. +@@ -440,10 +575,14 @@ static int mxl862xx_setup_link_local_tra + { + DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS); + struct dsa_port *dp = dsa_to_port(ds, port); ++ struct mxl862xx_priv *priv = ds->priv; + struct mxl862xx_pce_rule rule = {}; + int cpu_port = dp->cpu_dp->index; ++ struct mxl862xx_port *p; + int i; + ++ p = &priv->ports[port]; ++ + /* Address this port's CTP flow-table block */ + rule.logicalportid = port; + rule.subifidgroup = 0; +@@ -466,11 +605,18 @@ static int mxl862xx_setup_link_local_tra + rule.action.forward_port_map[i] = + cpu_to_le16(bitmap_read(portmap, i * 16, 16)); + ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && ++ p->cpu_egress_evlan.in_use) { ++ rule.action.extended_vlan_enable = 1; ++ rule.action.extended_vlan_block_id = ++ cpu_to_le16(p->cpu_egress_evlan.block_id); ++ } ++ + /* Bypass STP port state */ + rule.action.cross_state_action = + cpu_to_le32(MXL862XX_PCE_ACTION_CROSS_STATE_CROSS); + +- return MXL862XX_API_WRITE(ds->priv, MXL862XX_TFLOW_PCERULEWRITE, ++ return MXL862XX_API_WRITE(priv, MXL862XX_TFLOW_PCERULEWRITE, + rule); + } + +@@ -587,7 +733,8 @@ static int mxl862xx_sync_bridge_members( + __set_bit(member_dp->index, + priv->ports[port].portmap); + } +- __set_bit(dp->cpu_dp->index, priv->ports[port].portmap); ++ __set_bit(mxl862xx_cpu_bridge_port_id(ds, port), ++ priv->ports[port].portmap); + + err = mxl862xx_set_bridge_port(ds, port); + if (err) +@@ -762,7 +909,6 @@ static void mxl862xx_free_bridge(struct + + static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port) + { +- struct dsa_port *dp = dsa_to_port(ds, port); + struct mxl862xx_priv *priv = ds->priv; + int ret; + +@@ -774,15 +920,27 @@ static int mxl862xx_add_single_port_brid + + priv->ports[port].learning = false; + bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS); +- __set_bit(dp->cpu_dp->index, priv->ports[port].portmap); ++ __set_bit(mxl862xx_cpu_bridge_port_id(ds, port), ++ priv->ports[port].portmap); + + ret = mxl862xx_set_bridge_port(ds, port); + if (ret) + return ret; + +- /* Standalone ports should not flood unknown unicast or multicast +- * towards the CPU by default; only broadcast is needed initially. +- */ ++ /* In tag_8021q mode the TX path goes through the bridge engine ++ * (CTP ingress EVLAN reassigns to a virtual bridge port which ++ * then forwards via the bridge). With learning disabled on ++ * standalone ports, unknown unicast must be flooded so that ++ * frames from the host can reach the user port. ++ * ++ * In native SpTag mode, TX bypasses the bridge engine entirely ++ * (the special tag selects the egress port directly), so flood ++ * control only affects CPU-bound traffic and can be restrictive. ++ */ ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q) ++ return mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid, ++ true, true, true); ++ + return mxl862xx_bridge_config_fwd(ds, priv->ports[port].fid, + false, false, true); + } +@@ -790,10 +948,12 @@ static int mxl862xx_add_single_port_brid + static int mxl862xx_setup(struct dsa_switch *ds) + { + struct mxl862xx_priv *priv = ds->priv; +- int n_user_ports = 0, max_vlans; ++ int n_user_ports = 0, n_cpu_ports = 0, max_vlans; ++ int cpu_egress_rules, cpu_ingress_per_port; + int ingress_finals, vid_rules; ++ int egress_catchalls, evlan_reserved; + struct dsa_port *dp; +- int ret, i; ++ int ret, i, port; + + ret = mxl862xx_reset(priv); + if (ret) +@@ -806,7 +966,7 @@ static int mxl862xx_setup(struct dsa_swi + for (i = 0; i < 8; i++) + mxl862xx_setup_pcs(priv, &priv->serdes_ports[i], i + 9); + +- /* Calculate Extended VLAN block sizes. ++ /* Calculate Extended VLAN and VLAN Filter block sizes. + * With VLAN Filter handling VID membership checks: + * Ingress: only final catchall rules (PVID insertion, 802.1Q + * accept, non-8021Q TPID handling, discard). +@@ -814,40 +974,67 @@ static int mxl862xx_setup(struct dsa_swi + * ingress EVLAN rules are needed. (7 entries.) + * Egress: 2 rules per VID that needs tag stripping (untagged VIDs). + * No egress final catchalls -- VLAN Filter does the discard. +- * CPU: EVLAN is left disabled on CPU ports -- frames pass +- * through without EVLAN processing. ++ * ++ * tag_8021q mode reserves additional resources from the global ++ * pools for management VID handling: ++ * EVLAN: 5 egress rules per user port (on virtual bridge ports) ++ * + dynamically-sized CPU ingress EVLAN (2 per user port, ++ * budgeted here to guarantee space). ++ * VF: CPU port needs its own VF block for management VIDs. + * + * Total EVLAN budget: +- * n_user_ports * (ingress + egress) ≤ 1024. +- * Ingress blocks are small (7 entries), so almost all capacity +- * goes to egress VID rules. ++ * n_user_ports * (ingress + egress + cpu_egress + cpu_ingress_share) ++ * <= 1024. ++ * Total VF budget: ++ * (n_user_ports + n_cpu_ports) * vf_block_size <= 1024. + */ + dsa_switch_for_each_user_port(dp, ds) + n_user_ports++; ++ dsa_switch_for_each_cpu_port(dp, ds) ++ n_cpu_ports++; + + if (n_user_ports) { + ingress_finals = ARRAY_SIZE(ingress_aware_final); + vid_rules = ARRAY_SIZE(vid_accept_standard); ++ cpu_egress_rules = ARRAY_SIZE(cpu_egress_tag_8021q); ++ cpu_ingress_per_port = ARRAY_SIZE(cpu_ingress_reassign); ++ egress_catchalls = ARRAY_SIZE(tag_8021q_egress_strip); + + /* Ingress block: fixed at finals count (7 entries) */ + priv->evlan_ingress_size = ingress_finals; + ++ /* CPU port ingress EVLAN: reassign rules per user port */ ++ priv->cpu_evlan_ingress_size = ++ cpu_ingress_per_port * n_user_ports; ++ ++ /* Reserve EVLAN entries for tag_8021q: ++ * - virtual bridge port egress blocks (cpu_egress_rules each) ++ * - CPU port ingress EVLAN (cpu_ingress_per_port each) ++ * - user port egress catchalls for mgmt VID stripping ++ */ ++ evlan_reserved = n_user_ports * (ingress_finals + ++ cpu_egress_rules + ++ cpu_ingress_per_port + ++ egress_catchalls); ++ + /* Egress block: remaining budget divided equally among + * user ports. Each untagged VID needs vid_rules (2) + * EVLAN entries for tag stripping. Tagged-only VIDs +- * need no EVLAN rules at all. ++ * need no EVLAN rules at all. The block also includes ++ * space for the tag_8021q egress catchall rules. + */ +- max_vlans = (MXL862XX_TOTAL_EVLAN_ENTRIES - +- n_user_ports * ingress_finals) / ++ max_vlans = (MXL862XX_TOTAL_EVLAN_ENTRIES - evlan_reserved) / + (n_user_ports * vid_rules); +- priv->evlan_egress_size = vid_rules * max_vlans; ++ priv->evlan_egress_size = vid_rules * max_vlans + ++ egress_catchalls; + +- /* VLAN Filter block: one per user port. The 1024-entry +- * table is divided equally among user ports. Each port +- * gets its own VF block for per-port VID membership -- +- * discard_unmatched_tagged handles the rest. ++ /* VLAN Filter block: one per user port plus one per CPU ++ * port (used in tag_8021q mode for management VIDs). ++ * The 1024-entry table is divided equally among all ++ * consumers. + */ +- priv->vf_block_size = MXL862XX_TOTAL_VF_ENTRIES / n_user_ports; ++ priv->vf_block_size = MXL862XX_TOTAL_VF_ENTRIES / ++ (n_user_ports + n_cpu_ports); + } + + ret = mxl862xx_setup_drop_meter(ds); +@@ -858,6 +1045,68 @@ static int mxl862xx_setup(struct dsa_swi + dev_warn(ds->dev, "firmware < 1.0.80 installs global PCE rules " + "that interfere with DSA operation, please update\n"); + ++ /* Pre-allocate firmware resources for all ports. The DSA core ++ * calls change_tag_protocol() between setup() and port_setup(), ++ * and in tag_8021q mode that triggers dsa_tag_8021q_register() ++ * which fires tag_8021q_vlan_add callbacks that need EVLAN and ++ * VF blocks. complete_tag_8021q_setup() also needs per-port ++ * FIDs from add_single_port_bridge(). ++ * ++ * Per-port configuration (SpTag, CTP, portmaps, link-local ++ * traps) is deferred to port_setup(). ++ */ ++ dsa_switch_for_each_cpu_port(dp, ds) { ++ port = dp->index; ++ ++ mxl862xx_vf_init(&priv->ports[port].vf, ++ priv->vf_block_size); ++ mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan, ++ priv->cpu_evlan_ingress_size); ++ ret = mxl862xx_evlan_block_alloc(priv, ++ &priv->ports[port].ingress_evlan); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf); ++ if (ret) ++ return ret; ++ } ++ ++ dsa_switch_for_each_user_port(dp, ds) { ++ port = dp->index; ++ ++ mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan, ++ priv->evlan_ingress_size); ++ ret = mxl862xx_evlan_block_alloc(priv, ++ &priv->ports[port].ingress_evlan); ++ if (ret) ++ return ret; ++ ++ mxl862xx_evlan_block_init(&priv->ports[port].egress_evlan, ++ priv->evlan_egress_size); ++ ret = mxl862xx_evlan_block_alloc(priv, ++ &priv->ports[port].egress_evlan); ++ if (ret) ++ return ret; ++ ++ mxl862xx_vf_init(&priv->ports[port].vf, ++ priv->vf_block_size); ++ ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf); ++ if (ret) ++ return ret; ++ ++ mxl862xx_evlan_block_init(&priv->ports[port].cpu_egress_evlan, ++ ARRAY_SIZE(cpu_egress_tag_8021q)); ++ ret = mxl862xx_evlan_block_alloc(priv, ++ &priv->ports[port].cpu_egress_evlan); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_add_single_port_bridge(ds, port); ++ if (ret) ++ return ret; ++ } ++ + schedule_delayed_work(&priv->stats_work, + MXL862XX_STATS_POLL_INTERVAL); + +@@ -939,6 +1188,52 @@ static int mxl862xx_configure_sp_tag_pro + } + + /** ++ * mxl862xx_set_cpu_vbp - Push CPU-side virtual bridge port config to firmware ++ * @ds: DSA switch ++ * @port: user port index whose VBP to configure ++ * ++ * Each user port in tag_8021q mode has a virtual bridge port (VBP) that ++ * sits on the CPU RX path. The VBP lives in the user port's permanent ++ * per-port FID so host FDB/MDB entries in that FID can target it directly. ++ * Per-port host flood control is implemented via egress sub-meters on ++ * the VBP. ++ * ++ * This is intentionally separate from mxl862xx_set_bridge_port() because ++ * the VBP and the physical bridge port are independent firmware entities: ++ * host flood changes (deferred from atomic context) only need the VBP ++ * update, and VLAN/STP changes only need the physical bridge port update. ++ */ ++static int mxl862xx_set_cpu_vbp(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_bridge_port_config vbp_cfg = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ bool enable; ++ int i, idx; ++ ++ if (!p->bridge_port_cpu) ++ return 0; ++ ++ vbp_cfg.bridge_port_id = cpu_to_le16(p->bridge_port_cpu); ++ vbp_cfg.mask = cpu_to_le32( ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER); ++ vbp_cfg.bridge_id = cpu_to_le16(p->fid); ++ ++ for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) { ++ idx = mxl862xx_flood_meters[i]; ++ enable = !!(p->host_flood_block & BIT(idx)); ++ ++ vbp_cfg.egress_traffic_sub_meter_id[idx] = ++ enable ? cpu_to_le16(priv->drop_meter) : 0; ++ vbp_cfg.egress_sub_metering_enable[idx] = enable; ++ } ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, ++ vbp_cfg); ++} ++ ++/** + * mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware + * @priv: driver private data + * @block_id: HW Extended VLAN block ID +@@ -947,6 +1242,7 @@ static int mxl862xx_configure_sp_tag_pro + * @vid: VLAN ID for VID-specific rules (ignored when !desc->match_vid) + * @untagged: strip tag on egress for EVLAN_STRIP_IF_UNTAGGED action + * @pvid: port VLAN ID for PVID insertion rules (0 = no PVID) ++ * @mgmt_vid: tag_8021q management VID for outer tag insertion (0 = unused) + * + * Translates a compact rule descriptor into a full firmware + * mxl862xx_extendedvlan_config struct and writes it via the API. +@@ -954,7 +1250,8 @@ static int mxl862xx_configure_sp_tag_pro + static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv, + u16 block_id, u16 entry_index, + const struct mxl862xx_evlan_rule_desc *desc, +- u16 vid, bool untagged, u16 pvid) ++ u16 vid, bool untagged, u16 pvid, ++ u16 mgmt_vid) + { + struct mxl862xx_extendedvlan_config cfg = {}; + struct mxl862xx_extendedvlan_filter_vlan *fv; +@@ -1044,6 +1341,31 @@ static int mxl862xx_evlan_write_rule(str + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM); + } + break; ++ ++ case EVLAN_INSERT_OUTER: ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG); ++ cfg.treatment.add_outer_vlan = 1; ++ cfg.treatment.outer_vlan.vid_mode = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL); ++ cfg.treatment.outer_vlan.vid_val = cpu_to_le32(mgmt_vid); ++ cfg.treatment.outer_vlan.tpid = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q); ++ break; ++ ++ case EVLAN_STRIP1: ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG); ++ break; ++ ++ case EVLAN_REASSIGN: ++ cfg.treatment.remove_tag = ++ cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG); ++ cfg.treatment.reassign_bridge_port = 1; ++ cfg.treatment.new_bridge_port_id = ++ cpu_to_le16(desc->bridge_port_id); ++ break; ++ + } + + return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg); +@@ -1104,7 +1426,7 @@ static int mxl862xx_evlan_write_final_ru + for (i = 0; i < n_rules; i++) { + ret = mxl862xx_evlan_write_rule(priv, blk->block_id, + start_idx + i, &rules[i], +- 0, false, pvid); ++ 0, false, pvid, 0); + if (ret) + return ret; + } +@@ -1273,6 +1595,27 @@ static int mxl862xx_vf_del_vid(struct mx + } + + /** ++ * mxl862xx_vf_clear_vids - Remove all VID entries without freeing the HW block ++ * @priv: driver private data ++ * @vf: VLAN Filter block ++ * ++ * Frees the software VID list and resets active_count, but keeps the ++ * HW block allocated to avoid firmware table fragmentation. ++ */ ++static void mxl862xx_vf_clear_vids(struct mxl862xx_priv *priv, ++ struct mxl862xx_vf_block *vf) ++{ ++ struct mxl862xx_vf_vid *ve, *tmp; ++ ++ list_for_each_entry_safe(ve, tmp, &vf->vids, list) { ++ list_del(&ve->list); ++ kfree(ve); ++ } ++ ++ vf->active_count = 0; ++} ++ ++/** + * mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules + * @priv: driver private data + * @port: port number +@@ -1323,8 +1666,8 @@ static int mxl862xx_evlan_program_egress + const struct mxl862xx_evlan_rule_desc *vid_rules; + struct mxl862xx_vf_vid *vfv; + u16 old_active = blk->n_active; ++ int n_vid, n_catch, ret; + u16 idx = 0, i; +- int n_vid, ret; + + if (p->vlan_filtering) { + vid_rules = vid_accept_standard; +@@ -1341,13 +1684,23 @@ static int mxl862xx_evlan_program_egress + if (p->vlan_filtering && !vfv->untagged) + continue; + ++ /* Skip the tag_8021q management VID -- it must NOT get ++ * per-VID egress rules. The management VID arrives as ++ * the outer tag on CPU->user frames and is stripped by ++ * the catchall rules appended below. A per-VID rule ++ * here would match first (NO_FILTER outer) and prevent ++ * the catchall from stripping the tag. ++ */ ++ if (p->tag_8021q_vid && vfv->vid == p->tag_8021q_vid) ++ continue; ++ + if (idx + n_vid > blk->block_size) + return -ENOSPC; + + ret = mxl862xx_evlan_write_rule(priv, blk->block_id, + idx++, &vid_rules[0], + vfv->vid, vfv->untagged, +- p->pvid); ++ p->pvid, 0); + if (ret) + return ret; + +@@ -1356,7 +1709,29 @@ static int mxl862xx_evlan_program_egress + idx++, &vid_rules[1], + vfv->vid, + vfv->untagged, +- p->pvid); ++ p->pvid, 0); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ /* In tag_8021q mode, append catchall rules that strip the outer ++ * management VID tag from CPU->user frames. The management VID ++ * is kept through the forwarding pipeline (CPU ingress EVLAN ++ * only reassigns the bridge port, without stripping) and must ++ * be removed here before the frame exits the user port. ++ */ ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q) { ++ n_catch = ARRAY_SIZE(tag_8021q_egress_strip); ++ ++ if (idx + n_catch > blk->block_size) ++ return -ENOSPC; ++ ++ for (i = 0; i < n_catch; i++) { ++ ret = mxl862xx_evlan_write_rule(priv, blk->block_id, ++ idx++, ++ &tag_8021q_egress_strip[i], ++ 0, false, 0, 0); + if (ret) + return ret; + } +@@ -1368,8 +1743,7 @@ static int mxl862xx_evlan_program_egress + */ + for (i = idx; i < old_active; i++) { + ret = mxl862xx_evlan_deactivate_entry(priv, +- blk->block_id, +- i); ++ blk->block_id, i); + if (ret) + return ret; + } +@@ -1393,13 +1767,16 @@ static int mxl862xx_port_vlan_filtering( + + /* Reprogram Extended VLAN rules if filtering mode changed */ + if (changed) { +- /* When leaving VLAN-aware mode, release the ingress HW +- * block. The firmware passes frames through unchanged +- * when no ingress EVLAN block is assigned, so the block +- * is unnecessary in unaware mode. ++ /* When leaving VLAN-aware mode, disable the ingress ++ * EVLAN engine. The block stays allocated to avoid ++ * firmware EVLAN table fragmentation. + */ +- if (!vlan_filtering) ++ if (!vlan_filtering) { + p->ingress_evlan.in_use = false; ++ ret = mxl862xx_set_bridge_port(ds, port); ++ if (ret) ++ return ret; ++ } + + ret = mxl862xx_evlan_program_ingress(priv, port); + if (ret) +@@ -1536,18 +1913,19 @@ static int mxl862xx_setup_cpu_bridge(str + + /* include all assigned user ports in the CPU portmap */ + bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS); +- dsa_switch_for_each_user_port(dp, ds) { +- /* it's safe to rely on cpu_dp being valid for user ports */ +- if (dp->cpu_dp->index != port) +- continue; ++ if (priv->tag_proto != DSA_TAG_PROTO_MXL862_8021Q) { ++ dsa_switch_for_each_user_port(dp, ds) { ++ /* it's safe to rely on cpu_dp being valid for user ports */ ++ if (dp->cpu_dp->index != port) ++ continue; + +- __set_bit(dp->index, p->portmap); ++ __set_bit(dp->index, p->portmap); ++ } + } + + return mxl862xx_set_bridge_port(ds, port); + } + +- + static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port, + const struct dsa_bridge bridge, + bool *tx_fwd_offload, +@@ -1580,7 +1958,6 @@ static int mxl862xx_port_bridge_join(str + static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port, + const struct dsa_bridge bridge) + { +- struct dsa_port *dp = dsa_to_port(ds, port); + struct mxl862xx_priv *priv = ds->priv; + struct mxl862xx_port *p = &priv->ports[port]; + int err; +@@ -1595,34 +1972,587 @@ static void mxl862xx_port_bridge_leave(s + * single-port bridge + */ + bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS); +- __set_bit(dp->cpu_dp->index, p->portmap); ++ __set_bit(mxl862xx_cpu_bridge_port_id(ds, port), p->portmap); + p->flood_block = 0; ++ p->host_flood_block = 0; + +- /* Detach EVLAN and VF blocks from the bridge port BEFORE freeing +- * them. The firmware tracks a usage count per block and rejects +- * FREE while the count is non-zero. ++ /* Reset VLAN state for standalone mode. Ingress EVLAN is not ++ * needed outside a VLAN-aware bridge. Egress EVLAN is ++ * reprogrammed below -- in tag_8021q mode it gets the ++ * management VID strip catchalls, in SpTag mode it is cleared. + * +- * For EVLAN: setting in_use=false makes set_bridge_port send +- * enable=false, which decrements the firmware refcount. +- * +- * For VF: set_bridge_port sees dp->bridge == NULL (DSA already +- * cleared it) and sends vlan_filter_enable=0, which decrements +- * the firmware VF refcount. ++ * Do NOT clear the VF VID list here. Bridge VLANs are already ++ * removed by port_vlan_del during the switchdev replay in ++ * dsa_port_pre_bridge_leave. The remaining VIDs (e.g. the ++ * tag_8021q management VID) must survive bridge leave. + */ + p->pvid = 0; + p->ingress_evlan.in_use = false; +- p->egress_evlan.in_use = false; + ++ err = mxl862xx_evlan_program_egress(priv, port); ++ if (err) ++ dev_err(ds->dev, ++ "failed to restore egress EVLAN on port %d: %pe\n", ++ port, ERR_PTR(err)); ++ ++ /* Push the complete standalone port state to firmware. The ++ * firmware compares old vs new EVLAN/VF enable flags and adjusts ++ * block refcounts accordingly, so a single call suffices. ++ */ + err = mxl862xx_set_bridge_port(ds, port); + if (err) + dev_err(ds->dev, + "failed to update bridge port %d state: %pe\n", port, + ERR_PTR(err)); + ++ err = mxl862xx_set_cpu_vbp(ds, port); ++ if (err) ++ dev_err(ds->dev, ++ "failed to update CPU VBP for port %d: %pe\n", port, ++ ERR_PTR(err)); ++ + if (!dsa_bridge_ports(ds, bridge.dev)) + mxl862xx_free_bridge(ds, &bridge); + } + ++static int mxl862xx_setup_virtual_bridge_port(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_bridge_port_alloc bp_alloc = {}; ++ struct mxl862xx_bridge_port_config bp_cfg = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ struct dsa_port *cpu_dp; ++ int ret; ++ ++ cpu_dp = dsa_to_port(ds, port)->cpu_dp; ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_BRIDGEPORT_ALLOC, bp_alloc); ++ if (ret) { ++ dev_err(ds->dev, ++ "failed to allocate virtual bridge port for port %d: %pe\n", ++ port, ERR_PTR(ret)); ++ return ret; ++ } ++ ++ priv->ports[port].bridge_port_cpu = le16_to_cpu(bp_alloc.bridge_port_id); ++ ++ bp_cfg.bridge_port_id = bp_alloc.bridge_port_id; ++ bp_cfg.mask = cpu_to_le32( ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING); ++ bp_cfg.bridge_id = cpu_to_le16(priv->ports[port].fid); ++ bp_cfg.src_mac_learning_disable = 1; ++ bp_cfg.dest_logical_port_id = cpu_dp->index; ++ mxl862xx_fw_portmap_set_bit(bp_cfg.bridge_port_map, port); ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, bp_cfg); ++ if (ret) ++ dev_err(ds->dev, ++ "failed to configure virtual bridge port %u for port %d: %pe\n", ++ priv->ports[port].bridge_port_cpu, port, ERR_PTR(ret)); ++ ++ return ret; ++} ++ ++static void mxl862xx_free_virtual_bridge_port(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_bridge_port_alloc bp_alloc = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ int ret; ++ ++ if (!priv->ports[port].bridge_port_cpu) ++ return; ++ ++ mxl862xx_tag_8021q_disable_cpu_egress(ds, port); ++ ++ bp_alloc.bridge_port_id = cpu_to_le16(priv->ports[port].bridge_port_cpu); ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_FREE, bp_alloc); ++ if (ret) ++ dev_err(ds->dev, ++ "failed to free virtual bridge port %u for port %d: %pe\n", ++ priv->ports[port].bridge_port_cpu, port, ERR_PTR(ret)); ++ else ++ priv->ports[port].bridge_port_cpu = 0; ++} ++ ++static int mxl862xx_setup_tag_8021q(struct dsa_switch *ds) ++{ ++ struct dsa_port *dp; ++ int ret; ++ ++ dsa_switch_for_each_user_port(dp, ds) { ++ ret = mxl862xx_setup_virtual_bridge_port(ds, dp->index); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void mxl862xx_teardown_tag_8021q(struct dsa_switch *ds) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct dsa_port *dp; ++ int cpu; ++ ++ dsa_switch_for_each_user_port(dp, ds) { ++ mxl862xx_free_virtual_bridge_port(ds, dp->index); ++ priv->ports[dp->index].tag_8021q_vid = 0; ++ } ++ ++ /* Disable CPU port EVLAN engine and clear VF VID entries. ++ * The HW blocks stay allocated (freed in port_teardown). ++ */ ++ dsa_switch_for_each_cpu_port(dp, ds) { ++ cpu = dp->index; ++ ++ priv->ports[cpu].ingress_evlan.in_use = false; ++ mxl862xx_set_cpu_ctp_ingress_evlan(ds, cpu); ++ mxl862xx_vf_clear_vids(priv, &priv->ports[cpu].vf); ++ } ++ ++} ++ ++/** ++ * mxl862xx_tag_8021q_program_cpu_egress - Program virtual bridge port egress EVLAN ++ * @ds: DSA switch ++ * @port: user port whose virtual bridge port needs programming ++ * ++ * Programs the egress EVLAN block on the virtual bridge port associated ++ * with @port. The block is pre-allocated in port_setup. The rules insert the ++ * port's tag_8021q management VID as an outer 802.1Q tag on all ++ * frames exiting toward the CPU through this virtual bridge port. ++ */ ++static int mxl862xx_tag_8021q_program_cpu_egress(struct dsa_switch *ds, ++ int port) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ struct mxl862xx_evlan_block *blk = &p->cpu_egress_evlan; ++ struct mxl862xx_bridge_port_config bp_cfg = {}; ++ int n_rules = ARRAY_SIZE(cpu_egress_tag_8021q); ++ int i, ret; ++ ++ if (!p->bridge_port_cpu || !p->tag_8021q_vid) ++ return 0; ++ ++ for (i = 0; i < n_rules; i++) { ++ ret = mxl862xx_evlan_write_rule(priv, blk->block_id, ++ i, &cpu_egress_tag_8021q[i], ++ 0, false, 0, ++ p->tag_8021q_vid); ++ if (ret) ++ return ret; ++ } ++ ++ blk->n_active = n_rules; ++ blk->in_use = true; ++ ++ /* Enable egress EVLAN on the virtual bridge port */ ++ bp_cfg.bridge_port_id = cpu_to_le16(p->bridge_port_cpu); ++ bp_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN); ++ bp_cfg.egress_extended_vlan_enable = 1; ++ bp_cfg.egress_extended_vlan_block_id = cpu_to_le16(blk->block_id); ++ bp_cfg.egress_extended_vlan_block_size = cpu_to_le16(n_rules); ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, bp_cfg); ++} ++ ++/** ++ * mxl862xx_tag_8021q_cpu_vlan_program - Reprogram CPU port ingress EVLAN ++ * @ds: DSA switch ++ * ++ * Rebuilds the CPU port ingress EVLAN block with reassign rules for ++ * every tag_8021q VID currently in use. Called whenever a tag_8021q ++ * VID is added or removed. ++ * ++ * Each user port with a non-zero tag_8021q_vid gets 2 rules: ++ * - outer VID match + inner present: reassign to virtual bridge port ++ * - outer VID match + no inner: reassign to virtual bridge port ++ * ++ * The EVLAN block is assigned to the CPU port's CTP (not its bridge ++ * port) via CTP_PORTCONFIGSET, matching the reference and legacy ++ * driver architecture. ++ */ ++static int mxl862xx_tag_8021q_cpu_vlan_program(struct dsa_switch *ds) ++{ ++ struct mxl862xx_evlan_rule_desc rule; ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_evlan_block *blk; ++ struct dsa_port *cpu_dp, *dp; ++ struct mxl862xx_port *p; ++ u16 idx, old_active, vid; ++ int cpu, ret, i; ++ ++ dsa_switch_for_each_cpu_port(cpu_dp, ds) ++ break; ++ ++ cpu = cpu_dp->index; ++ blk = &priv->ports[cpu].ingress_evlan; ++ ++ old_active = blk->n_active; ++ idx = 0; ++ ++ dsa_switch_for_each_user_port(dp, ds) { ++ p = &priv->ports[dp->index]; ++ vid = p->tag_8021q_vid; ++ ++ if (!vid) ++ continue; ++ ++ for (i = 0; i < ARRAY_SIZE(cpu_ingress_reassign); i++) { ++ rule = cpu_ingress_reassign[i]; ++ ++ rule.bridge_port_id = p->bridge_port_cpu; ++ ret = mxl862xx_evlan_write_rule(priv, blk->block_id, ++ idx++, &rule, vid, ++ false, 0, 0); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ blk->n_active = idx; ++ ++ /* Deactivate stale entries beyond the new active range */ ++ for (; idx < old_active; idx++) { ++ ret = mxl862xx_evlan_deactivate_entry(priv, blk->block_id, ++ idx); ++ if (ret) ++ return ret; ++ } ++ blk->in_use = blk->n_active > 0; ++ ++ return mxl862xx_set_cpu_ctp_ingress_evlan(ds, cpu); ++} ++ ++static int mxl862xx_tag_8021q_cpu_vlan_add(struct dsa_switch *ds, int port, ++ u16 vid) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ int ret; ++ ++ /* Add VID to CPU port's VF block so firmware accepts frames ++ * tagged with this VID on CPU port ingress. ++ */ ++ ret = mxl862xx_vf_add_vid(priv, &priv->ports[port].vf, vid, false); ++ if (ret) ++ return ret; ++ ++ return mxl862xx_tag_8021q_cpu_vlan_program(ds); ++} ++ ++static int mxl862xx_tag_8021q_cpu_vlan_del(struct dsa_switch *ds, int port, ++ u16 vid) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ int ret; ++ ++ ret = mxl862xx_vf_del_vid(priv, &priv->ports[port].vf, vid); ++ if (ret) ++ return ret; ++ ++ return mxl862xx_tag_8021q_cpu_vlan_program(ds); ++} ++ ++static int mxl862xx_tag_8021q_vlan_add(struct dsa_switch *ds, int port, ++ u16 vid, u16 flags) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ int ret; ++ ++ if (dsa_is_cpu_port(ds, port)) ++ return mxl862xx_tag_8021q_cpu_vlan_add(ds, port, vid); ++ ++ /* User port: store the tag_8021q VID and add to VF block */ ++ priv->ports[port].tag_8021q_vid = vid; ++ ++ ret = mxl862xx_vf_add_vid(priv, &priv->ports[port].vf, vid, false); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_tag_8021q_program_cpu_egress(ds, port); ++ if (ret) ++ return ret; ++ ++ /* Rebuild CPU ingress EVLAN to include this port's management VID. ++ * The DSA framework may call the CPU port's tag_8021q_vlan_add ++ * before this user port's callback (ports iterate in index order), ++ * so the CPU ingress EVLAN rebuild triggered by the CPU callback ++ * might have run before tag_8021q_vid was set. Rebuild now to ++ * ensure this port's reassignment rule is present. ++ */ ++ return mxl862xx_tag_8021q_cpu_vlan_program(ds); ++} ++ ++static int mxl862xx_tag_8021q_vlan_del(struct dsa_switch *ds, int port, ++ u16 vid) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ ++ if (dsa_is_cpu_port(ds, port)) ++ return mxl862xx_tag_8021q_cpu_vlan_del(ds, port, vid); ++ ++ if (priv->ports[port].tag_8021q_vid == vid) { ++ priv->ports[port].tag_8021q_vid = 0; ++ mxl862xx_tag_8021q_disable_cpu_egress(ds, port); ++ } ++ ++ return mxl862xx_vf_del_vid(priv, &priv->ports[port].vf, vid); ++} ++ ++/** ++ * mxl862xx_refresh_cpu_targets - Update portmaps and traps for new CPU target ++ * @ds: DSA switch ++ * ++ * After switching between SpTag and tag_8021q, the CPU-side target in ++ * each user port's portmap changes (physical CPU port vs. virtual ++ * bridge port). This rebuilds every user port's portmap with the ++ * correct CPU target and reinstalls the link-local PCE trap. ++ */ ++static int mxl862xx_refresh_cpu_targets(struct dsa_switch *ds) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct dsa_port *dp, *member_dp; ++ struct mxl862xx_port *p; ++ int ret, port; ++ ++ dsa_switch_for_each_user_port(dp, ds) { ++ port = dp->index; ++ p = &priv->ports[port]; ++ ++ bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS); ++ if (dp->bridge) { ++ ++ dsa_switch_for_each_bridge_member(member_dp, ds, dp->bridge->dev) { ++ if (member_dp->index != port) ++ __set_bit(member_dp->index, p->portmap); ++ } ++ } ++ __set_bit(mxl862xx_cpu_bridge_port_id(ds, port), p->portmap); ++ ++ /* Reprogram user port egress EVLAN to add or remove the ++ * tag_8021q management VID strip catchalls. ++ */ ++ ret = mxl862xx_evlan_program_egress(priv, port); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_set_bridge_port(ds, port); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_setup_link_local_trap(ds, port); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * mxl862xx_complete_tag_8021q_setup - Finish deferred tag_8021q initialization ++ * @ds: DSA switch ++ * ++ * Called from change_tag_protocol() to configure the firmware for ++ * tag_8021q mode. Requires each user port to already have an FID ++ * (from add_single_port_bridge in setup()). Reconfigures CPU ports, ++ * allocates virtual bridge ports and enables flooding on standalone ++ * bridges. Link-local traps are refreshed separately after ++ * dsa_tag_8021q_register() has set cpu_egress_evlan.in_use. ++ */ ++static int mxl862xx_complete_tag_8021q_setup(struct dsa_switch *ds) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct dsa_port *dp; ++ int ret, port; ++ ++ /* Disable SpTag and reduce to a single CTP on CPU ports for ++ * 8021q mode. Without a special tag the PMAC cannot select a ++ * sub-CTP, so only CTP 0 must exist. ++ */ ++ dsa_switch_for_each_cpu_port(dp, ds) { ++ ret = mxl862xx_configure_sp_tag_proto(ds, dp->index, false); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_configure_ctp_port(ds, dp->index, ++ dp->index, 1); ++ if (ret) ++ return ret; ++ ++ ret = mxl862xx_setup_cpu_bridge(ds, dp->index); ++ if (ret) ++ return ret; ++ } ++ ++ ret = mxl862xx_setup_tag_8021q(ds); ++ if (ret) ++ return ret; ++ ++ /* In tag_8021q mode TX goes through the bridge engine (CTP ++ * ingress EVLAN reassigns to a virtual bridge port), so ++ * unknown unicast and multicast must be flooded at the bridge ++ * level for frames from the CPU to reach user ports. The ++ * per-port bridges may have been created with flooding ++ * disabled (SpTag mode default), so update them now. ++ * ++ * Block unknown UC and MC on the VBP egress meters so frames ++ * to unknown destinations are not flooded to the host. DSA ++ * core will selectively enable host flooding via ++ * port_set_host_flood when needed (e.g. promisc mode). ++ */ ++ dsa_switch_for_each_user_port(dp, ds) { ++ port = dp->index; ++ ++ if (dp->bridge) ++ continue; ++ ++ ret = mxl862xx_bridge_config_fwd(ds, ++ priv->ports[port].fid, ++ true, true, true); ++ if (ret) ++ return ret; ++ ++ priv->ports[port].host_flood_block = ++ BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC) | ++ BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP) | ++ BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP); ++ ++ ret = mxl862xx_set_cpu_vbp(ds, port); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int mxl862xx_change_tag_protocol(struct dsa_switch *ds, ++ enum dsa_tag_protocol proto) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ enum dsa_tag_protocol old_proto = priv->tag_proto; ++ struct dsa_port *dp; ++ int ret, port; ++ ++ /* Flush all MAC entries on tag protocol change. Host entries ++ * installed via portmap (tag_8021q VBP-based) vs single port_id ++ * (SpTag) are not compatible across modes. ++ */ ++ if (ds->setup) ++ mxl862xx_api_wrap(priv, MXL862XX_MAC_TABLECLEAR, ++ NULL, 0, false, false); ++ ++ /* Set tag_proto early so that helpers called below (e.g. ++ * mxl862xx_setup_cpu_bridge) see the target protocol. ++ * Restored on failure. ++ */ ++ priv->tag_proto = proto; ++ ++ switch (proto) { ++ case DSA_TAG_PROTO_MXL862: ++ if (ds->tag_8021q_ctx) { ++ dsa_tag_8021q_unregister(ds); ++ mxl862xx_teardown_tag_8021q(ds); ++ ++ /* Virtual bridge ports are gone; revert portmaps ++ * and traps to target the physical CPU port. ++ */ ++ ret = mxl862xx_refresh_cpu_targets(ds); ++ if (ret) ++ goto err_restore; ++ ++ /* Revert standalone bridges to SpTag mode ++ * defaults: discard unknown UC/MC (SpTag TX ++ * bypasses bridge engine) while keeping ++ * broadcast flooding. ++ */ ++ dsa_switch_for_each_user_port(dp, ds) { ++ port = dp->index; ++ ++ if (dp->bridge) ++ continue; ++ ++ mxl862xx_bridge_config_fwd(ds, ++ priv->ports[port].fid, ++ false, false, true); ++ } ++ } ++ dsa_switch_for_each_cpu_port(dp, ds) { ++ ret = mxl862xx_configure_sp_tag_proto(ds, dp->index, ++ true); ++ if (ret) ++ goto err_restore; ++ ++ /* Restore multiple CTPs so the special tag's ++ * sub_if_id can select per-port sub-CTPs. ++ */ ++ ret = mxl862xx_configure_ctp_port(ds, dp->index, ++ dp->index, ++ 32 - dp->index); ++ if (ret) ++ goto err_restore; ++ ++ /* Restore CPU portmap: SpTag mode needs all user ++ * ports in the CPU's bridge_port_map. tag_8021q ++ * mode clears it to prevent FID 0 flooding. ++ */ ++ ret = mxl862xx_setup_cpu_bridge(ds, dp->index); ++ if (ret) ++ goto err_restore; ++ } ++ break; ++ ++ case DSA_TAG_PROTO_MXL862_8021Q: ++ ret = mxl862xx_complete_tag_8021q_setup(ds); ++ if (ret) ++ goto err_restore; ++ ++ /* RTNL is held by the DSA core when calling ++ * change_tag_protocol(), both during initial setup ++ * and at runtime. ++ */ ++ ret = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q)); ++ if (ret) { ++ mxl862xx_teardown_tag_8021q(ds); ++ goto err_restore; ++ } ++ ++ /* Refresh link-local traps now that tag_8021q_vlan_add ++ * callbacks have set cpu_egress_evlan.in_use, so the ++ * PCE rules get the correct EVLAN treatment. ++ */ ++ ret = mxl862xx_refresh_cpu_targets(ds); ++ if (ret) { ++ dsa_tag_8021q_unregister(ds); ++ mxl862xx_teardown_tag_8021q(ds); ++ goto err_restore; ++ } ++ break; ++ ++ default: ++ ret = -EPROTONOSUPPORT; ++ goto err_restore; ++ } ++ ++ return 0; ++ ++err_restore: ++ priv->tag_proto = old_proto; ++ return ret; ++} ++ ++static void mxl862xx_teardown(struct dsa_switch *ds) ++{ ++ /* tag_8021q teardown is handled in mxl862xx_remove() under ++ * RTNL, before dsa_unregister_switch() takes dsa2_mutex. ++ * dsa_tag_8021q_unregister() needs RTNL for vlan_vid_del(), ++ * and acquiring RTNL inside teardown() (which runs under ++ * dsa2_mutex) would invert the RTNL -> dsa2_mutex lock order. ++ */ ++} ++ + static int mxl862xx_port_setup(struct dsa_switch *ds, int port) + { + struct mxl862xx_priv *priv = ds->priv; +@@ -1642,55 +2572,30 @@ static int mxl862xx_port_setup(struct ds + dsa_port_is_dsa(dp)) + return 0; + +- /* configure tag protocol */ +- ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port); ++ /* configure tag protocol: SpTag for native, disable for 8021q */ ++ ret = mxl862xx_configure_sp_tag_proto(ds, port, ++ is_cpu_port && ++ priv->tag_proto == DSA_TAG_PROTO_MXL862); + if (ret) + return ret; + + /* assign CTP port IDs */ + ret = mxl862xx_configure_ctp_port(ds, port, port, +- is_cpu_port ? 32 - port : 1); ++ (is_cpu_port && ++ priv->tag_proto == DSA_TAG_PROTO_MXL862) ? ++ 32 - port : 1); + if (ret) + return ret; + + if (is_cpu_port) +- /* assign user ports to CPU port bridge */ + return mxl862xx_setup_cpu_bridge(ds, port); + +- /* setup single-port bridge for user ports */ +- ret = mxl862xx_add_single_port_bridge(ds, port); +- if (ret) +- return ret; +- + /* install link-local trap for this user port */ + ret = mxl862xx_setup_link_local_trap(ds, port); + if (ret) + return ret; + +- /* Initialize and pre-allocate per-port EVLAN and VF blocks for +- * user ports. CPU ports do not use EVLAN or VF -- frames pass +- * through without processing. Pre-allocation avoids firmware +- * EVLAN table fragmentation and simplifies control flow. +- */ +- mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan, +- priv->evlan_ingress_size); +- ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan); +- if (ret) +- return ret; +- +- mxl862xx_evlan_block_init(&priv->ports[port].egress_evlan, +- priv->evlan_egress_size); +- ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].egress_evlan); +- if (ret) +- return ret; +- +- mxl862xx_vf_init(&priv->ports[port].vf, priv->vf_block_size); +- ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf); +- if (ret) +- return ret; +- + priv->ports[port].setup_done = true; +- + return 0; + } + +@@ -1712,7 +2617,7 @@ static void mxl862xx_port_teardown(struc + priv->ports[port].setup_done = false; + } + +-static int mxl862xx_get_fid(struct dsa_switch *ds, struct dsa_db db) ++static int mxl862xx_get_fid(struct dsa_switch *ds, const struct dsa_db db) + { + struct mxl862xx_priv *priv = ds->priv; + +@@ -1730,23 +2635,244 @@ static int mxl862xx_get_fid(struct dsa_s + } + } + +-static int mxl862xx_port_fdb_add(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid, struct dsa_db db) ++/** ++ * mxl862xx_fdb_bridge_port - Translate port for MAC table in tag_8021q mode ++ * @ds: DSA switch ++ * @port: port number passed by DSA (usually the CPU port for host entries) ++ * @db: database context identifying the user port or bridge ++ * ++ * In tag_8021q mode, host FDB/MDB entries for standalone ports must use ++ * the virtual bridge port (bridge_port_cpu) as the MAC table destination ++ * so that known-unicast and known-multicast frames exit through the ++ * virtual bridge port's egress EVLAN, which inserts the management VID. ++ * Without this, the firmware forwards known traffic directly to the ++ * physical CPU bridge port, bypassing management VID insertion, and DSA ++ * drops the untagged frame. ++ */ ++static int mxl862xx_fdb_bridge_port(struct dsa_switch *ds, int port, ++ const struct dsa_db db) + { +- struct mxl862xx_mac_table_add param = {}; +- int fid = mxl862xx_get_fid(ds, db), ret; + struct mxl862xx_priv *priv = ds->priv; ++ u16 bp_cpu; + +- if (fid < 0) +- return fid; ++ if (dsa_is_cpu_port(ds, port) && priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && ++ db.type == DSA_DB_PORT) { ++ bp_cpu = priv->ports[db.dp->index].bridge_port_cpu; ++ ++ if (bp_cpu) ++ return bp_cpu; ++ } ++ ++ return port; ++} ++ ++/** ++ * mxl862xx_fdb_add_per_fid - Install a unicast FDB entry in one FID ++ */ ++static int mxl862xx_fdb_add_per_fid(struct dsa_switch *ds, ++ const unsigned char *addr, u16 vid, ++ u16 fid, int port_id) ++{ ++ struct mxl862xx_mac_table_add param = {}; ++ struct mxl862xx_priv *priv = ds->priv; + +- param.port_id = cpu_to_le32(port); ++ param.port_id = cpu_to_le32(port_id); + param.static_entry = true; + param.fid = cpu_to_le16(fid); + param.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); + ether_addr_copy(param.mac, addr); + +- ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, param); ++ return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, param); ++} ++ ++/** ++ * mxl862xx_fdb_del_per_fid - Remove a unicast FDB entry from one FID ++ */ ++static int mxl862xx_fdb_del_per_fid(struct dsa_switch *ds, ++ const unsigned char *addr, u16 vid, ++ u16 fid) ++{ ++ struct mxl862xx_mac_table_remove param = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ ++ param.fid = cpu_to_le16(fid); ++ param.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); ++ ether_addr_copy(param.mac, addr); ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, param); ++} ++ ++/** ++ * mxl862xx_mac_portmap_add - Set port bits in a MAC table entry's portmap ++ * @priv: driver private data ++ * @addr: MAC address ++ * @fid: firmware FID ++ * @vid: VLAN ID ++ * @add_map: firmware-format portmap of bits to set ++ * ++ * Queries the existing MAC table entry by {addr, fid, vid}. If found, ++ * the existing portmap is preserved and @add_map bits are OR'd in. ++ * The entry is then written back as a static portmap-mode entry. ++ */ ++static int mxl862xx_mac_portmap_add(struct mxl862xx_priv *priv, ++ const unsigned char *addr, ++ u16 fid, u16 vid, ++ const __le16 *add_map) ++{ ++ struct mxl862xx_mac_table_query qparam = {}; ++ struct mxl862xx_mac_table_add aparam = {}; ++ int i, ret; ++ ++ ether_addr_copy(qparam.mac, addr); ++ qparam.fid = cpu_to_le16(fid); ++ qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYQUERY, qparam); ++ if (ret) ++ return ret; ++ ++ ether_addr_copy(aparam.mac, addr); ++ aparam.fid = cpu_to_le16(fid); ++ aparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); ++ aparam.static_entry = true; ++ aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG); ++ ++ if (qparam.found) ++ memcpy(aparam.port_map, qparam.port_map, ++ sizeof(aparam.port_map)); ++ ++ for (i = 0; i < ARRAY_SIZE(aparam.port_map); i++) ++ aparam.port_map[i] |= add_map[i]; ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, aparam); ++} ++ ++/** ++ * mxl862xx_mac_portmap_del - Clear port bits from a MAC table entry's portmap ++ * @priv: driver private data ++ * @addr: MAC address ++ * @fid: firmware FID ++ * @vid: VLAN ID ++ * @del_map: firmware-format portmap of bits to clear ++ * ++ * Queries the existing MAC table entry. If not found, returns 0. ++ * Clears all @del_map bits from the portmap. If the portmap becomes ++ * empty, the entry is removed entirely; otherwise it is updated. ++ */ ++static int mxl862xx_mac_portmap_del(struct mxl862xx_priv *priv, ++ const unsigned char *addr, ++ u16 fid, u16 vid, ++ const __le16 *del_map) ++{ ++ struct mxl862xx_mac_table_remove rparam = {}; ++ struct mxl862xx_mac_table_query qparam = {}; ++ struct mxl862xx_mac_table_add aparam = {}; ++ int i, ret; ++ ++ ether_addr_copy(qparam.mac, addr); ++ qparam.fid = cpu_to_le16(fid); ++ qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYQUERY, qparam); ++ if (ret) ++ return ret; ++ ++ if (!qparam.found) ++ return 0; ++ ++ for (i = 0; i < ARRAY_SIZE(qparam.port_map); i++) ++ qparam.port_map[i] &= ~del_map[i]; ++ ++ if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) { ++ ether_addr_copy(rparam.mac, addr); ++ rparam.fid = cpu_to_le16(fid); ++ rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, ++ vid)); ++ return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, ++ rparam); ++ } ++ ++ ether_addr_copy(aparam.mac, addr); ++ aparam.fid = cpu_to_le16(fid); ++ aparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); ++ aparam.static_entry = true; ++ aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG); ++ memcpy(aparam.port_map, qparam.port_map, sizeof(aparam.port_map)); ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, aparam); ++} ++ ++/** ++ * mxl862xx_mac_add_host_bridge - Install a host FDB/MDB entry with VBP portmap ++ * @ds: DSA switch ++ * @addr: MAC address ++ * @vid: VLAN ID ++ * @bridge: bridge whose members' VBPs to include ++ * ++ * In tag_8021q mode, host FDB/MDB entries in a shared bridge FID must use ++ * portmap mode targeting ALL bridge members' virtual bridge ports (VBPs). ++ * The firmware ANDs the entry's portmap with each ingress port's ++ * bridge_port_map, which contains only that port's own VBP. This ++ * selects the correct VBP per ingress port, ensuring frames exit ++ * through the right egress EVLAN (which inserts the per-port management ++ * VID that identifies the source port to DSA on the CPU side). ++ */ ++static int mxl862xx_mac_add_host_bridge(struct dsa_switch *ds, ++ const unsigned char *addr, u16 vid, ++ const struct dsa_bridge *bridge) ++{ ++ __le16 add_map[MXL862XX_FW_PORTMAP_WORDS] = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ u16 fid = priv->bridges[bridge->num]; ++ struct dsa_port *member_dp; ++ ++ dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev) ++ mxl862xx_fw_portmap_set_bit(add_map, ++ priv->ports[member_dp->index].bridge_port_cpu); ++ ++ return mxl862xx_mac_portmap_add(priv, addr, fid, vid, add_map); ++} ++ ++static int mxl862xx_port_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid, const struct dsa_db db) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct dsa_port *target_dp; ++ int fid, ret; ++ ++ /* tag_8021q host FDB for bridged ports: portmap with all VBPs */ ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && dsa_is_cpu_port(ds, port) && ++ db.type == DSA_DB_BRIDGE) { ++ if (!priv->bridges[db.bridge.num]) ++ return -ENOENT; ++ ++ return mxl862xx_mac_add_host_bridge(ds, addr, vid, &db.bridge); ++ } ++ ++ /* tag_8021q standalone host FDB for bridged ports: also mirror ++ * into the bridge FID. DSA installs VID-specific host entries ++ * via the standalone path (DSA_DB_PORT), but with IVL enabled ++ * the firmware needs matching entries in the bridge FID for ++ * VID-keyed lookups to succeed. ++ */ ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && dsa_is_cpu_port(ds, port) && ++ db.type == DSA_DB_PORT && vid > 0) { ++ target_dp = dsa_to_port(ds, db.dp->index); ++ ++ if (target_dp->bridge) { ++ ret = mxl862xx_mac_add_host_bridge(ds, addr, vid, ++ target_dp->bridge); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ fid = mxl862xx_get_fid(ds, db); ++ if (fid < 0) ++ return fid; ++ ++ ret = mxl862xx_fdb_add_per_fid(ds, addr, vid, fid, ++ mxl862xx_fdb_bridge_port(ds, port, db)); + if (ret) + dev_err(ds->dev, "failed to add FDB entry on port %d\n", port); + +@@ -1756,18 +2882,25 @@ static int mxl862xx_port_fdb_add(struct + static int mxl862xx_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, const struct dsa_db db) + { +- struct mxl862xx_mac_table_remove param = {}; +- int fid = mxl862xx_get_fid(ds, db), ret; + struct mxl862xx_priv *priv = ds->priv; ++ struct dsa_port *target_dp; ++ int fid, ret; + ++ /* Mirror of the standalone->bridge FID path in fdb_add */ ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && dsa_is_cpu_port(ds, port) && ++ db.type == DSA_DB_PORT && vid > 0) { ++ target_dp = dsa_to_port(ds, db.dp->index); ++ ++ if (target_dp->bridge && priv->bridges[target_dp->bridge->num]) ++ mxl862xx_fdb_del_per_fid(ds, addr, vid, ++ priv->bridges[target_dp->bridge->num]); ++ } ++ ++ fid = mxl862xx_get_fid(ds, db); + if (fid < 0) + return fid; + +- param.fid = cpu_to_le16(fid); +- param.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, vid)); +- ether_addr_copy(param.mac, addr); +- +- ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, param); ++ ret = mxl862xx_fdb_del_per_fid(ds, addr, vid, fid); + if (ret) + dev_err(ds->dev, "failed to remove FDB entry on port %d\n", port); + +@@ -1806,88 +2939,147 @@ static int mxl862xx_port_fdb_dump(struct + return 0; + } + ++/** ++ * mxl862xx_mdb_add_to_fid - Add a port bit to an MDB entry in one FID ++ * @ds: DSA switch ++ * @mdb: multicast group address and VID ++ * @fid: firmware FID to operate on ++ * @port_bit: port index to set in the portmap ++ * @vid: VLAN ID for the MAC table entry ++ */ ++static int mxl862xx_mdb_add_to_fid(struct dsa_switch *ds, ++ const struct switchdev_obj_port_mdb *mdb, ++ u16 fid, int port_bit, u16 vid) ++{ ++ __le16 add_map[MXL862XX_FW_PORTMAP_WORDS] = {}; ++ ++ mxl862xx_fw_portmap_set_bit(add_map, port_bit); ++ ++ return mxl862xx_mac_portmap_add(ds->priv, mdb->addr, fid, vid, ++ add_map); ++} ++ ++/** ++ * mxl862xx_mdb_del_from_fid - Remove a port bit from an MDB entry in one FID ++ * @ds: DSA switch ++ * @mdb: multicast group address ++ * @fid: firmware FID to operate on ++ * @port_bit: port index to clear from the portmap ++ * @vid: VLAN ID for the MAC table entry (0 for SVL/tag_8021q mode) ++ */ ++static int mxl862xx_mdb_del_from_fid(struct dsa_switch *ds, ++ const struct switchdev_obj_port_mdb *mdb, ++ u16 fid, int port_bit, u16 vid) ++{ ++ __le16 del_map[MXL862XX_FW_PORTMAP_WORDS] = {}; ++ ++ mxl862xx_fw_portmap_set_bit(del_map, port_bit); ++ ++ return mxl862xx_mac_portmap_del(ds->priv, mdb->addr, fid, vid, ++ del_map); ++} ++ + static int mxl862xx_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + const struct dsa_db db) + { +- struct mxl862xx_mac_table_query qparam = {}; +- struct mxl862xx_mac_table_add aparam = {}; + struct mxl862xx_priv *priv = ds->priv; + int fid, ret; + ++ /* tag_8021q host MDB for bridged ports: portmap with all VBPs */ ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && dsa_is_cpu_port(ds, port) && ++ db.type == DSA_DB_BRIDGE) { ++ if (!priv->bridges[db.bridge.num]) ++ return -ENOENT; ++ ++ return mxl862xx_mac_add_host_bridge(ds, mdb->addr, ++ mdb->vid, &db.bridge); ++ } ++ + fid = mxl862xx_get_fid(ds, db); + if (fid < 0) + return fid; + +- /* Look up existing entry by {MAC, FID, TCI} */ +- ether_addr_copy(qparam.mac, mdb->addr); +- qparam.fid = cpu_to_le16(fid); +- qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); +- +- ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYQUERY, qparam); ++ ret = mxl862xx_mdb_add_to_fid(ds, mdb, fid, ++ mxl862xx_fdb_bridge_port(ds, port, db), ++ mdb->vid); + if (ret) + return ret; + +- /* Build the ADD command using portmap mode */ +- ether_addr_copy(aparam.mac, mdb->addr); +- aparam.fid = cpu_to_le16(fid); +- aparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); +- aparam.static_entry = true; +- aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG); ++ /* In tag_8021q mode, standalone host MDB entries need both the VBP ++ * and the physical port in the portmap. The TX path goes through ++ * the bridge engine (CPU -> VBP -> MAC lookup), so source-port ++ * filtering would remove the sole VBP entry, dropping the frame. ++ * With both bits set: ++ * TX: VBP source-filtered -> physical port remains -> frame exits ++ * RX: physical port source-filtered -> VBP remains -> CPU receives ++ */ ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && db.type == DSA_DB_PORT) ++ ret = mxl862xx_mdb_add_to_fid(ds, mdb, fid, db.dp->index, ++ mdb->vid); + +- /* Merge with existing portmap if entry already exists */ +- if (qparam.found) +- memcpy(aparam.port_map, qparam.port_map, +- sizeof(aparam.port_map)); ++ return ret; ++} + +- mxl862xx_fw_portmap_set_bit(aparam.port_map, port); ++/** ++ * mxl862xx_mac_del_host_bridge - Remove VBP bits from a host FDB/MDB entry ++ * @ds: DSA switch ++ * @addr: MAC address ++ * @vid: VLAN ID ++ * @bridge: bridge whose members' VBPs to clear ++ * ++ * Clears all bridge member VBP bits from the portmap. If the portmap ++ * becomes empty (no user-port bits remain), removes the entry entirely. ++ */ ++static int mxl862xx_mac_del_host_bridge(struct dsa_switch *ds, ++ const unsigned char *addr, u16 vid, ++ const struct dsa_bridge *bridge) ++{ ++ __le16 del_map[MXL862XX_FW_PORTMAP_WORDS] = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ u16 fid = priv->bridges[bridge->num]; ++ struct dsa_port *member_dp; + +- return MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, aparam); ++ dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev) ++ mxl862xx_fw_portmap_set_bit(del_map, ++ priv->ports[member_dp->index].bridge_port_cpu); ++ ++ return mxl862xx_mac_portmap_del(priv, addr, fid, vid, del_map); + } + + static int mxl862xx_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + const struct dsa_db db) + { +- struct mxl862xx_mac_table_remove rparam = {}; +- struct mxl862xx_mac_table_query qparam = {}; +- struct mxl862xx_mac_table_add aparam = {}; +- int fid = mxl862xx_get_fid(ds, db), ret; + struct mxl862xx_priv *priv = ds->priv; ++ int fid, ret; ++ ++ /* tag_8021q host MDB for bridged ports: clear all VBP bits */ ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && dsa_is_cpu_port(ds, port) && ++ db.type == DSA_DB_BRIDGE) { ++ if (!priv->bridges[db.bridge.num]) ++ return -ENOENT; ++ ++ return mxl862xx_mac_del_host_bridge(ds, mdb->addr, ++ mdb->vid, &db.bridge); ++ } + ++ fid = mxl862xx_get_fid(ds, db); + if (fid < 0) + return fid; + +- /* Look up existing entry */ +- qparam.fid = cpu_to_le16(fid); +- qparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); +- ether_addr_copy(qparam.mac, mdb->addr); +- +- ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYQUERY, qparam); ++ ret = mxl862xx_mdb_del_from_fid(ds, mdb, fid, ++ mxl862xx_fdb_bridge_port(ds, port, db), ++ mdb->vid); + if (ret) + return ret; + +- if (!qparam.found) +- return 0; +- +- mxl862xx_fw_portmap_clear_bit(qparam.port_map, port); +- +- if (mxl862xx_fw_portmap_is_empty(qparam.port_map)) { +- /* No ports left -- remove the entry entirely */ +- rparam.fid = cpu_to_le16(fid); +- rparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); +- ether_addr_copy(rparam.mac, mdb->addr); +- ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, rparam); +- } else { +- /* Write back with reduced portmap */ +- aparam.fid = cpu_to_le16(fid); +- aparam.tci = cpu_to_le16(FIELD_PREP(MXL862XX_TCI_VLAN_ID, mdb->vid)); +- ether_addr_copy(aparam.mac, mdb->addr); +- aparam.static_entry = true; +- aparam.port_id = cpu_to_le32(MXL862XX_PORTMAP_FLAG); +- memcpy(aparam.port_map, qparam.port_map, sizeof(aparam.port_map)); +- ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, aparam); +- } ++ /* In tag_8021q mode, standalone host MDB entries have both the VBP ++ * and the physical port in the portmap -- remove both bits. ++ */ ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q && db.type == DSA_DB_PORT) ++ ret = mxl862xx_mdb_del_from_fid(ds, mdb, fid, db.dp->index, ++ mdb->vid); + + return ret; + } +@@ -1975,7 +3167,9 @@ static void mxl862xx_host_flood_work_fn( + struct mxl862xx_priv *priv = p->priv; + struct dsa_switch *ds = priv->ds; + int port = p - priv->ports; ++ unsigned long block; + bool uc, mc; ++ int ret; + + rtnl_lock(); + +@@ -1988,14 +3182,31 @@ static void mxl862xx_host_flood_work_fn( + uc = p->host_flood_uc; + mc = p->host_flood_mc; + +- /* The hardware controls unknown-unicast/multicast forwarding per FID +- * (bridge), not per source port. For bridged ports all members share +- * one FID, so we cannot selectively suppress flooding to the CPU for +- * one source port while allowing it for another. Silently ignore the +- * request -- the excess flooding towards the CPU is harmless. +- */ +- if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port))) +- mxl862xx_bridge_config_fwd(ds, p->fid, uc, mc, true); ++ if (priv->tag_proto == DSA_TAG_PROTO_MXL862_8021Q) { ++ block = 0; ++ ++ if (!uc) ++ block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC); ++ if (!mc) { ++ block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP); ++ block |= BIT(MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP); ++ } ++ ++ if (block != p->host_flood_block) { ++ p->host_flood_block = block; ++ ret = mxl862xx_set_cpu_vbp(ds, port); ++ if (ret) ++ dev_err(ds->dev, ++ "failed to set host flood on port %d: %pe\n", ++ port, ERR_PTR(ret)); ++ } ++ } else { ++ /* SpTag mode: per-FID forwarding, only works for ++ * standalone ports (each has its own FID). ++ */ ++ if (!dsa_port_bridge_dev_get(dsa_to_port(ds, port))) ++ mxl862xx_bridge_config_fwd(ds, p->fid, uc, mc, true); ++ } + + rtnl_unlock(); + } +@@ -2330,7 +3541,9 @@ static void mxl862xx_get_stats64(struct + + static const struct dsa_switch_ops mxl862xx_switch_ops = { + .get_tag_protocol = mxl862xx_get_tag_protocol, ++ .change_tag_protocol = mxl862xx_change_tag_protocol, + .setup = mxl862xx_setup, ++ .teardown = mxl862xx_teardown, + .port_setup = mxl862xx_port_setup, + .port_teardown = mxl862xx_port_teardown, + .phylink_get_caps = mxl862xx_phylink_get_caps, +@@ -2352,6 +3565,8 @@ static const struct dsa_switch_ops mxl86 + .port_vlan_filtering = mxl862xx_port_vlan_filtering, + .port_vlan_add = mxl862xx_port_vlan_add, + .port_vlan_del = mxl862xx_port_vlan_del, ++ .tag_8021q_vlan_add = mxl862xx_tag_8021q_vlan_add, ++ .tag_8021q_vlan_del = mxl862xx_tag_8021q_vlan_del, + .get_strings = mxl862xx_get_strings, + .get_sset_count = mxl862xx_get_sset_count, + .get_ethtool_stats = mxl862xx_get_ethtool_stats, +@@ -2399,6 +3614,8 @@ static int mxl862xx_probe(struct mdio_de + + INIT_DELAYED_WORK(&priv->stats_work, mxl862xx_stats_work_fn); + ++ priv->tag_proto = DSA_TAG_PROTO_MXL862; ++ + dev_set_drvdata(dev, ds); + + return dsa_register_switch(ds); +@@ -2415,16 +3632,29 @@ static void mxl862xx_remove(struct mdio_ + + priv = ds->priv; + ++ /* Tear down tag_8021q under RTNL before dsa_unregister_switch(). ++ * dsa_tag_8021q_unregister() calls vlan_vid_del() which needs ++ * RTNL. dsa_unregister_switch() takes dsa2_mutex, and other ++ * paths take RTNL -> dsa2_mutex, so RTNL must be acquired ++ * before dsa2_mutex to avoid lock inversion. ++ */ ++ if (ds->tag_8021q_ctx) { ++ rtnl_lock(); ++ dsa_tag_8021q_unregister(ds); ++ mxl862xx_teardown_tag_8021q(ds); ++ rtnl_unlock(); ++ } ++ + dsa_unregister_switch(ds); + + cancel_delayed_work_sync(&priv->stats_work); + + mxl862xx_host_shutdown(priv); + +- /* Cancel any pending host flood work. dsa_unregister_switch() ++ /* Cancel any pending host flood work. dsa_unregister_switch() + * has already called port_teardown (which sets setup_done=false), + * but a worker could still be blocked on rtnl_lock(). Since we +- * are now outside RTNL, cancel_work_sync() will not deadlock. ++ * are now outside RTNL, cancel_work_sync() won't deadlock. + */ + for (i = 0; i < MXL862XX_MAX_PORTS; i++) + cancel_work_sync(&priv->ports[i].host_flood_work); +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -8,8 +8,6 @@ + #include + #include + +-struct mxl862xx_priv; +- + #define MXL862XX_MAX_PORTS 17 + #define MXL862XX_DEFAULT_BRIDGE 0 + #define MXL862XX_MAX_BRIDGES 48 +@@ -20,6 +18,8 @@ struct mxl862xx_priv; + /* Number of __le16 words in a firmware portmap (128-bit bitmap). */ + #define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16) + ++struct mxl862xx_priv; ++ + /** + * mxl862xx_fw_portmap_from_bitmap - convert a kernel bitmap to a firmware + * portmap (__le16[8]) +@@ -210,6 +210,9 @@ struct mxl862xx_port_stats { + * @vf: per-port VLAN Filter block state + * @ingress_evlan: ingress extended VLAN block state + * @egress_evlan: egress extended VLAN block state ++ * @bridge_port_cpu: virtual bridge port ID for tag_8021q CPU-side CTP ++ * @host_flood_block: bitmask of firmware meter indices used to block ++ * host flooding on the virtual bridge port (tag_8021q) + * @host_flood_uc: desired host unicast flood state (true = flood); + * updated atomically by port_set_host_flood, consumed + * by the deferred host_flood_work +@@ -224,6 +227,7 @@ struct mxl862xx_port_stats { + * periodically by the stats polling work + * @stats_lock: protects accumulator reads in .get_stats64 against + * concurrent updates from the polling work ++ * @tag_8021q_vid: currently assigned tag_8021q management VID + */ + struct mxl862xx_port { + struct mxl862xx_priv *priv; +@@ -238,9 +242,14 @@ struct mxl862xx_port { + struct mxl862xx_vf_block vf; + struct mxl862xx_evlan_block ingress_evlan; + struct mxl862xx_evlan_block egress_evlan; ++ /* tag_8021q state */ ++ u16 bridge_port_cpu; ++ unsigned long host_flood_block; + bool host_flood_uc; + bool host_flood_mc; + struct work_struct host_flood_work; ++ u16 tag_8021q_vid; ++ struct mxl862xx_evlan_block cpu_egress_evlan; + /* Hardware stats accumulation */ + struct mxl862xx_port_stats stats; + spinlock_t stats_lock; +@@ -302,6 +311,7 @@ union mxl862xx_fw_version { + * errors + * @crc_err: set atomically before CRC-triggered shutdown, cleared + * after ++ * @tag_proto: active DSA tag protocol (native or 8021q) + * @drop_meter: index of the single shared zero-rate firmware meter + * used to unconditionally drop traffic (used to block + * flooding) +@@ -310,12 +320,13 @@ union mxl862xx_fw_version { + * @serdes_ports: SerDes interfaces incl. sub-interfaces in case of + * 10G_QXGMII + * @ports: per-port state, indexed by switch port number ++ * @evlan_ingress_size: per-port ingress Extended VLAN block size ++ * @evlan_egress_size: per-port egress Extended VLAN block size ++ * @cpu_evlan_ingress_size: CPU port ingress EVLAN block size (tag_8021q) + * @bridges: maps DSA bridge number to firmware bridge ID; + * zero means no firmware bridge allocated for that + * DSA bridge number. Indexed by dsa_bridge.num + * (0 .. ds->max_num_bridges). +- * @evlan_ingress_size: per-port ingress Extended VLAN block size +- * @evlan_egress_size: per-port egress Extended VLAN block size + * @vf_block_size: per-port VLAN Filter block size + * @stats_work: periodic work item that polls RMON hardware counters + * and accumulates them into 64-bit per-port stats +@@ -325,6 +336,7 @@ struct mxl862xx_priv { + struct mdio_device *mdiodev; + struct work_struct crc_err_work; + unsigned long crc_err; ++ enum dsa_tag_protocol tag_proto; + u16 drop_meter; + union mxl862xx_fw_version fw_version; + struct mxl862xx_pcs serdes_ports[8]; +@@ -332,6 +344,7 @@ struct mxl862xx_priv { + u16 bridges[MXL862XX_MAX_BRIDGES + 1]; + u16 evlan_ingress_size; + u16 evlan_egress_size; ++ u16 cpu_evlan_ingress_size; + u16 vf_block_size; + struct delayed_work stats_work; + }; +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -56,6 +56,8 @@ struct tc_action; + #define DSA_TAG_PROTO_VSC73XX_8021Q_VALUE 28 + #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29 + #define DSA_TAG_PROTO_MXL862_VALUE 30 ++#define DSA_TAG_PROTO_MXL862_8021Q_VALUE 31 ++ + + enum dsa_tag_protocol { + DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, +@@ -89,6 +91,7 @@ enum dsa_tag_protocol { + DSA_TAG_PROTO_LAN937X = DSA_TAG_PROTO_LAN937X_VALUE, + DSA_TAG_PROTO_VSC73XX_8021Q = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE, + DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE, ++ DSA_TAG_PROTO_MXL862_8021Q = DSA_TAG_PROTO_MXL862_8021Q_VALUE, + }; + + struct dsa_switch; +--- a/net/dsa/Kconfig ++++ b/net/dsa/Kconfig +@@ -111,6 +111,13 @@ config NET_DSA_TAG_MXL_862XX + MaxLinear MxL86252 and MxL86282 switches using their native 8-byte + tagging protocol. + ++config NET_DSA_TAG_MXL_862XX_8021Q ++ tristate "Tag driver for MaxLinear MxL862xx switches, using VLAN" ++ help ++ Say Y or M if you want to enable support for tagging frames for the ++ MaxLinear MxL86252 and MxL86282 switches using 802.1Q VLAN-based ++ tagging instead of their native 8-byte tagging protocol. ++ + config NET_DSA_TAG_KSZ + tristate "Tag driver for Microchip 8795/937x/9477/9893 families of switches" + help +--- a/net/dsa/Makefile ++++ b/net/dsa/Makefile +@@ -29,6 +29,7 @@ obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz + obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o + obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o + obj-$(CONFIG_NET_DSA_TAG_MXL_862XX) += tag_mxl862xx.o ++obj-$(CONFIG_NET_DSA_TAG_MXL_862XX_8021Q) += tag_mxl862xx_8021q.o + obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o + obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o + obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o +--- /dev/null ++++ b/net/dsa/tag_mxl862xx_8021q.c +@@ -0,0 +1,59 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * DSA 802.1Q-based tag driver for MaxLinear MxL862xx switches ++ * ++ * Uses the DSA tag_8021q framework to encode port information in ++ * 802.1Q VLAN tags instead of the native 8-byte MxL862xx special tag. ++ * ++ * Copyright (C) 2025 Daniel Golle ++ */ ++ ++#include ++ ++#include "tag.h" ++#include "tag_8021q.h" ++ ++#define MXL862_8021Q_NAME "mxl862xx-8021q" ++ ++static struct sk_buff *mxl862_8021q_xmit(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ struct dsa_port *dp = dsa_user_to_port(netdev); ++ u16 tx_vid = dsa_tag_8021q_standalone_vid(dp); ++ u16 queue_mapping = skb_get_queue_mapping(skb); ++ u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); ++ ++ return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q, ++ (pcp << VLAN_PRIO_SHIFT) | tx_vid); ++} ++ ++static struct sk_buff *mxl862_8021q_rcv(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ int src_port = -1, switch_id = -1; ++ ++ dsa_8021q_rcv(skb, &src_port, &switch_id, NULL, NULL); ++ ++ skb->dev = dsa_conduit_find_user(netdev, switch_id, src_port); ++ if (!skb->dev) ++ return NULL; ++ ++ dsa_default_offload_fwd_mark(skb); ++ ++ return skb; ++} ++ ++static const struct dsa_device_ops mxl862_8021q_netdev_ops = { ++ .name = MXL862_8021Q_NAME, ++ .proto = DSA_TAG_PROTO_MXL862_8021Q, ++ .xmit = mxl862_8021q_xmit, ++ .rcv = mxl862_8021q_rcv, ++ .needed_headroom = VLAN_HLEN, ++ .promisc_on_conduit = true, ++}; ++ ++MODULE_DESCRIPTION("DSA tag driver for MaxLinear MxL862xx switches, using VLAN"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862_8021Q, MXL862_8021Q_NAME); ++ ++module_dsa_tag_driver(mxl862_8021q_netdev_ops); diff --git a/target/linux/generic/pending-6.12/760-17-net-dsa-mxl862xx-add-link-aggregation-support.patch b/target/linux/generic/pending-6.12/760-17-net-dsa-mxl862xx-add-link-aggregation-support.patch new file mode 100644 index 00000000000..d373f6511fd --- /dev/null +++ b/target/linux/generic/pending-6.12/760-17-net-dsa-mxl862xx-add-link-aggregation-support.patch @@ -0,0 +1,862 @@ +From 31359e8b7673e656d0591a9eb5014b45911383ae Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 03:44:41 +0000 +Subject: [PATCH 26/35] net: dsa: mxl862xx: add link aggregation support + +Implement LAG offloading via the firmware's trunking engine. A +dedicated firmware bridge port is allocated per LAG and remains +stable for the LAG's lifetime. All member CTPs redirect to it, +and FDB/MDB entries target it, so no entry migration is needed +when the LAG master (lowest-numbered member port) changes. + +The firmware provides three cooperating mechanisms: + + - PCE_TRUNK_CONF register: global 6-bit hash field selection + (SA, DA, SIP, DIP, sport, dport) + - CTP redirection: all member CTPs point bridge_port_id to the + LAG's dedicated bridge port + - P-mapper on the LAG bridge port: 64 hash-indexed entries + (indices 9..72) filled round-robin with active member ports + +Hash and active-backup bond modes are supported. The global hash +register is widened monotonically on LAG join and recomputed from +stored per-port requirements on LAG leave. + +The LAG master's full bridge port configuration (bridge_id, EVLAN, +VLAN filter, learning, portmap, flood metering) is pushed to the +LAG bridge port via __mxl862xx_set_bridge_port() whenever it +changes. Bridge portmaps use the LAG bridge port ID instead of +individual member port indices, ensuring correct cross-LAG +forwarding. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 22 + + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 4 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 587 +++++++++++++++++++++++- + drivers/net/dsa/mxl862xx/mxl862xx.h | 34 ++ + 4 files changed, 630 insertions(+), 17 deletions(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -564,6 +564,28 @@ struct mxl862xx_pmapper { + } __packed; + + /** ++ * struct mxl862xx_trunking_cfg - LAG hash algorithm configuration ++ * @ip_src: Include source IP address in trunk hash (1 = include) ++ * @ip_dst: Include destination IP address in trunk hash ++ * @mac_src: Include source MAC address in trunk hash ++ * @mac_dst: Include destination MAC address in trunk hash ++ * @src_port: Include TCP/UDP source port in trunk hash ++ * @dst_port: Include TCP/UDP destination port in trunk hash ++ * ++ * The firmware inverts the boolean sense when writing the hardware ++ * register (PCE_TRUNK_CONF): bit=0 means include, bit=1 means exclude. ++ * This struct uses the logical sense (1 = include). ++ */ ++struct mxl862xx_trunking_cfg { ++ u8 ip_src; ++ u8 ip_dst; ++ u8 mac_src; ++ u8 mac_dst; ++ u8 src_port; ++ u8 dst_port; ++} __packed; ++ ++/** + * struct mxl862xx_bridge_port_config - Bridge Port Configuration + * @bridge_port_id: Bridge Port ID allocated by bridge port allocation + * @mask: See &enum mxl862xx_bridge_port_config_mask +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -51,6 +51,7 @@ + + #define MXL862XX_QOS_METERCFGSET (MXL862XX_QOS_MAGIC + 0x2) + #define MXL862XX_QOS_METERALLOC (MXL862XX_QOS_MAGIC + 0x2a) ++#define MXL862XX_QOS_PMAPPERTABLESET (MXL862XX_QOS_MAGIC + 0x2e) + + #define MXL862XX_RMON_PORT_GET (MXL862XX_RMON_MAGIC + 0x1) + +@@ -73,6 +74,9 @@ + + #define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x2) + ++#define MXL862XX_TRUNKING_MAGIC 0xe00 ++#define MXL862XX_TRUNKING_CFGSET (MXL862XX_TRUNKING_MAGIC + 0x2) ++ + #define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2) + + #define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x1) +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -620,7 +620,42 @@ static int mxl862xx_setup_link_local_tra + rule); + } + +-static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) ++static bool mxl862xx_is_lag_master(const struct mxl862xx_priv *priv, int port) ++{ ++ struct dsa_lag *lag = priv->ports[port].lag; ++ int i; ++ ++ if (!lag) ++ return true; ++ ++ for (i = 0; i < port; i++) { ++ if (priv->ports[i].lag == lag) ++ return false; ++ } ++ ++ return true; ++} ++ ++/** ++ * mxl862xx_lag_bridge_port - Get the effective bridge port ID for a port ++ * @priv: driver private data ++ * @port: port index ++ * ++ * If @port is a member of a LAG, returns the LAG's dedicated firmware ++ * bridge port ID. Otherwise returns @port itself. ++ */ ++static u16 mxl862xx_lag_bridge_port(const struct mxl862xx_priv *priv, int port) ++{ ++ struct dsa_lag *lag = priv->ports[port].lag; ++ ++ if (lag && priv->lag_bridge_ports[lag->id]) ++ return priv->lag_bridge_ports[lag->id]; ++ ++ return port; ++} ++ ++static int __mxl862xx_set_bridge_port(struct dsa_switch *ds, int port, ++ u16 bp_id) + { + struct mxl862xx_bridge_port_config br_port_cfg = {}; + struct dsa_port *dp = dsa_to_port(ds, port); +@@ -632,7 +667,7 @@ static int mxl862xx_set_bridge_port(stru + bool enable; + int i, idx; + +- br_port_cfg.bridge_port_id = cpu_to_le16(port); ++ br_port_cfg.bridge_port_id = cpu_to_le16(bp_id); + br_port_cfg.bridge_id = cpu_to_le16(bridge_id); + br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | +@@ -715,12 +750,38 @@ static int mxl862xx_set_bridge_port(stru + br_port_cfg); + } + ++static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ u16 lag_bp; ++ int ret; ++ ++ ret = __mxl862xx_set_bridge_port(ds, port, port); ++ if (ret) ++ return ret; ++ ++ /* If this port is a LAG master, also push its config to the ++ * LAG's dedicated bridge port (which is the actual target of ++ * all member CTP redirections). ++ */ ++ if (p->lag && mxl862xx_is_lag_master(priv, port)) { ++ lag_bp = priv->lag_bridge_ports[p->lag->id]; ++ if (lag_bp) ++ ret = __mxl862xx_set_bridge_port(ds, port, lag_bp); ++ } ++ ++ return ret; ++} ++ + static int mxl862xx_sync_bridge_members(struct dsa_switch *ds, + const struct dsa_bridge *bridge) + { + struct mxl862xx_priv *priv = ds->priv; + struct dsa_port *dp, *member_dp; +- int port, err, ret = 0; ++ struct mxl862xx_port *p; ++ int port, member, err, ret = 0; ++ u16 lag_bp, bp; + + dsa_switch_for_each_bridge_member(dp, ds, bridge->dev) { + port = dp->index; +@@ -729,9 +790,21 @@ static int mxl862xx_sync_bridge_members( + MXL862XX_MAX_BRIDGE_PORTS); + + dsa_switch_for_each_bridge_member(member_dp, ds, bridge->dev) { +- if (member_dp->index != port) +- __set_bit(member_dp->index, +- priv->ports[port].portmap); ++ member = member_dp->index; ++ ++ /* For LAG members, only include the LAG's ++ * dedicated bridge port in the portmap. ++ * Non-master members are skipped to avoid ++ * duplicates (they share the same LAG bridge ++ * port). ++ */ ++ if (!mxl862xx_is_lag_master(priv, member)) ++ continue; ++ if (member != port) { ++ bp = mxl862xx_lag_bridge_port(priv, ++ member); ++ __set_bit(bp, priv->ports[port].portmap); ++ } + } + __set_bit(mxl862xx_cpu_bridge_port_id(ds, port), + priv->ports[port].portmap); +@@ -741,6 +814,25 @@ static int mxl862xx_sync_bridge_members( + ret = err; + } + ++ /* Push updated portmaps to LAG bridge ports. Each LAG master's ++ * portmap (which excludes itself) is used for the LAG bridge ++ * port -- this naturally avoids self-forwarding. ++ */ ++ dsa_switch_for_each_bridge_member(dp, ds, bridge->dev) { ++ p = &priv->ports[dp->index]; ++ ++ if (!p->lag || !mxl862xx_is_lag_master(priv, dp->index)) ++ continue; ++ ++ lag_bp = priv->lag_bridge_ports[p->lag->id]; ++ if (!lag_bp) ++ continue; ++ ++ err = __mxl862xx_set_bridge_port(ds, dp->index, lag_bp); ++ if (err) ++ ret = err; ++ } ++ + return ret; + } + +@@ -1926,6 +2018,408 @@ static int mxl862xx_setup_cpu_bridge(str + return mxl862xx_set_bridge_port(ds, port); + } + ++/** ++ * mxl862xx_lag_master_port - Find the LAG master (lowest-numbered member) ++ * @ds: DSA switch ++ * @lag: LAG to search ++ * ++ * The master's bridge port hosts the P-mapper and receives all ingress ++ * traffic via CTP redirection from other members. ++ * ++ * Return: port index of the master, or -ENOENT if no members. ++ */ ++static int mxl862xx_lag_master_port(struct dsa_switch *ds, ++ const struct dsa_lag *lag) ++{ ++ struct dsa_port *dp; ++ int master = -ENOENT; ++ ++ dsa_lag_foreach_port(dp, ds->dst, lag) { ++ if (dp->ds != ds) ++ continue; ++ if (master < 0 || dp->index < master) ++ master = dp->index; ++ } ++ ++ return master; ++} ++ ++/** ++ * mxl862xx_lag_hash_bits - Translate Linux hash mode to firmware hash bitmask ++ * @info: bonding upper info (tx_type + hash_type) ++ * ++ * Return: 6-bit hash field bitmask (MXL862XX_TRUNK_HASH_*), or negative ++ * errno if the mode is unsupported. ++ */ ++static int mxl862xx_lag_hash_bits(const struct netdev_lag_upper_info *info) ++{ ++ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) ++ return 0; ++ ++ switch (info->hash_type) { ++ case NETDEV_LAG_HASH_L2: ++ return MXL862XX_TRUNK_HASH_SA | MXL862XX_TRUNK_HASH_DA; ++ case NETDEV_LAG_HASH_L34: ++ return MXL862XX_TRUNK_HASH_SIP | MXL862XX_TRUNK_HASH_DIP | ++ MXL862XX_TRUNK_HASH_SPORT | MXL862XX_TRUNK_HASH_DPORT; ++ case NETDEV_LAG_HASH_L23: ++ case NETDEV_LAG_HASH_E23: ++ return MXL862XX_TRUNK_HASH_SA | MXL862XX_TRUNK_HASH_DA | ++ MXL862XX_TRUNK_HASH_SIP | MXL862XX_TRUNK_HASH_DIP; ++ case NETDEV_LAG_HASH_E34: ++ return MXL862XX_TRUNK_HASH_SA | MXL862XX_TRUNK_HASH_DA | ++ MXL862XX_TRUNK_HASH_SIP | MXL862XX_TRUNK_HASH_DIP | ++ MXL862XX_TRUNK_HASH_SPORT | MXL862XX_TRUNK_HASH_DPORT; ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++/** ++ * mxl862xx_lag_set_hash - Push trunk hash configuration to firmware ++ * @priv: driver private data ++ * @hash_bits: 6-bit hash field bitmask (MXL862XX_TRUNK_HASH_*) ++ * ++ * Only issues a firmware command when @hash_bits differs from the ++ * currently active configuration. ++ */ ++static int mxl862xx_lag_set_hash(struct mxl862xx_priv *priv, u8 hash_bits) ++{ ++ struct mxl862xx_trunking_cfg cfg = {}; ++ ++ if (priv->trunk_hash == hash_bits) ++ return 0; ++ ++ cfg.mac_src = !!(hash_bits & MXL862XX_TRUNK_HASH_SA); ++ cfg.mac_dst = !!(hash_bits & MXL862XX_TRUNK_HASH_DA); ++ cfg.ip_src = !!(hash_bits & MXL862XX_TRUNK_HASH_SIP); ++ cfg.ip_dst = !!(hash_bits & MXL862XX_TRUNK_HASH_DIP); ++ cfg.src_port = !!(hash_bits & MXL862XX_TRUNK_HASH_SPORT); ++ cfg.dst_port = !!(hash_bits & MXL862XX_TRUNK_HASH_DPORT); ++ ++ priv->trunk_hash = hash_bits; ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_TRUNKING_CFGSET, cfg); ++} ++ ++/** ++ * mxl862xx_lag_recompute_hash - Recompute global hash from all active LAGs ++ * @ds: DSA switch ++ * ++ * Scans all ports and ORs together the stored hash requirements of every ++ * active LAG member. Used after a LAG is destroyed to potentially narrow ++ * the global hash configuration. ++ * ++ * Return: union of all active LAGs' hash field bitmasks. ++ */ ++static u8 mxl862xx_lag_recompute_hash(struct dsa_switch *ds) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ u8 hash = 0; ++ int port; ++ ++ for (port = 0; port < ds->num_ports; port++) { ++ if (priv->ports[port].lag) ++ hash |= priv->ports[port].lag_hash_bits; ++ } ++ ++ return hash; ++} ++ ++/** ++ * mxl862xx_lag_build_pmapper - Fill P-mapper with round-robin LAG distribution ++ * @ds: DSA switch ++ * @lag: LAG group ++ * @pm: P-mapper struct to fill (entries 9..72) ++ * ++ * Only ports with lag_tx_enabled are included. Falls back to the ++ * master port if no members are active. ++ */ ++static void mxl862xx_lag_build_pmapper(struct dsa_switch *ds, ++ const struct dsa_lag *lag, ++ struct mxl862xx_pmapper *pm) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ int active_ports[MXL862XX_MAX_PORTS]; ++ int n_active = 0, master, port; ++ struct dsa_port *dp; ++ int i; ++ ++ dsa_lag_foreach_port(dp, ds->dst, lag) { ++ if (dp->ds != ds) ++ continue; ++ if (priv->ports[dp->index].lag_tx_enabled) ++ active_ports[n_active++] = dp->index; ++ } ++ ++ /* Fallback: if no members are active, use the master port */ ++ if (!n_active) { ++ master = mxl862xx_lag_master_port(ds, lag); ++ ++ if (master >= 0) { ++ active_ports[0] = master; ++ n_active = 1; ++ } ++ } ++ ++ if (!n_active) ++ return; ++ ++ for (i = 0; i < MXL862XX_PMAPPER_LAG_COUNT; i++) { ++ port = active_ports[i % n_active]; ++ ++ pm->dest_sub_if_id_group[MXL862XX_PMAPPER_LAG_FIRST + i] = ++ (port << 4) & 0xff; ++ } ++} ++ ++/** ++ * mxl862xx_lag_redirect_ctp - Redirect a port's CTP to the LAG master ++ * @priv: driver private data ++ * @port: port whose CTP to redirect ++ * @master_port: LAG master port index ++ */ ++static int mxl862xx_lag_redirect_ctp(struct mxl862xx_priv *priv, ++ int port, int master_port) ++{ ++ struct mxl862xx_ctp_port_config ctp = {}; ++ ++ ctp.logical_port_id = port; ++ ctp.mask = cpu_to_le32(MXL862XX_CTP_PORT_CONFIG_MASK_BRIDGE_PORT_ID); ++ ctp.bridge_port_id = cpu_to_le16(master_port); ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_CTP_PORTCONFIGSET, ctp); ++} ++ ++/** ++ * mxl862xx_lag_restore_ctp - Restore a port's CTP to point to itself ++ * @priv: driver private data ++ * @port: port whose CTP to restore ++ */ ++static int mxl862xx_lag_restore_ctp(struct mxl862xx_priv *priv, int port) ++{ ++ return mxl862xx_lag_redirect_ctp(priv, port, port); ++} ++ ++/** ++ * mxl862xx_lag_disable_pmapper - Disable P-mapper on a bridge port ++ * @ds: DSA switch ++ * @bp_id: firmware bridge port ID to reconfigure ++ */ ++static int mxl862xx_lag_disable_pmapper(struct dsa_switch *ds, u16 bp_id) ++{ ++ struct mxl862xx_bridge_port_config bp_cfg = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ ++ bp_cfg.bridge_port_id = cpu_to_le16(bp_id); ++ bp_cfg.mask = cpu_to_le32( ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING); ++ bp_cfg.dest_logical_port_id = bp_id; ++ bp_cfg.pmapper_enable = 0; ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, bp_cfg); ++} ++ ++/** ++ * mxl862xx_lag_sync - Synchronize LAG hardware state for a LAG group ++ * @ds: DSA switch ++ * @lag: LAG group to synchronize ++ * ++ * Finds the master (lowest-numbered member), redirects all member CTPs ++ * to the LAG's dedicated firmware bridge port, configures the P-mapper ++ * for hash distribution, and pushes the master's full bridge port ++ * configuration (EVLAN, VF, portmap, learning) to the LAG bridge port. ++ */ ++static int mxl862xx_lag_sync(struct dsa_switch *ds, const struct dsa_lag *lag) ++{ ++ struct mxl862xx_bridge_port_config bp_cfg = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_pmapper pm = {}; ++ struct dsa_port *dp; ++ int master, ret; ++ u16 lag_bp; ++ ++ lag_bp = priv->lag_bridge_ports[lag->id]; ++ if (!lag_bp) ++ return -ENOENT; ++ ++ master = mxl862xx_lag_master_port(ds, lag); ++ if (master < 0) ++ return master; ++ ++ /* Redirect all member CTPs to the LAG bridge port */ ++ dsa_lag_foreach_port(dp, ds->dst, lag) { ++ if (dp->ds != ds) ++ continue; ++ ret = mxl862xx_lag_redirect_ctp(priv, dp->index, lag_bp); ++ if (ret) ++ return ret; ++ } ++ ++ /* Push the master's full config to the LAG bridge port so it ++ * inherits the current bridge_id, EVLAN/VF blocks, portmap, ++ * learning and flood settings. ++ */ ++ ret = __mxl862xx_set_bridge_port(ds, master, lag_bp); ++ if (ret) ++ return ret; ++ ++ /* Build P-mapper with active members */ ++ mxl862xx_lag_build_pmapper(ds, lag, &pm); ++ ++ /* Enable P-mapper in LAG mode on the LAG bridge port */ ++ bp_cfg.bridge_port_id = cpu_to_le16(lag_bp); ++ bp_cfg.mask = cpu_to_le32( ++ MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING); ++ bp_cfg.dest_logical_port_id = master; ++ bp_cfg.pmapper_enable = 1; ++ bp_cfg.pmapper_mapping_mode = ++ cpu_to_le32(MXL862XX_PMAPPER_MAPPING_LAG); ++ bp_cfg.pmapper = pm; ++ ++ return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, bp_cfg); ++} ++ ++static int mxl862xx_port_lag_join(struct dsa_switch *ds, int port, ++ const struct dsa_lag lag, ++ struct netdev_lag_upper_info *info, ++ struct netlink_ext_ack *extack) ++{ ++ struct mxl862xx_bridge_port_alloc bp_alloc = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ int hash_bits; ++ u8 new_hash; ++ int ret; ++ ++ if (dsa_is_cpu_port(ds, port)) { ++ NL_SET_ERR_MSG_MOD(extack, "CPU port LAG not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH && ++ info->tx_type != NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) { ++ NL_SET_ERR_MSG_MOD(extack, "Only hash and active-backup LAG modes supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ hash_bits = mxl862xx_lag_hash_bits(info); ++ if (hash_bits < 0) { ++ NL_SET_ERR_MSG_MOD(extack, "Unsupported LAG hash mode"); ++ return hash_bits; ++ } ++ ++ /* Allocate a dedicated firmware bridge port for this LAG on ++ * first member join. This bridge port is stable for the ++ * LAG's lifetime -- all CTP redirections, FDB and MDB entries ++ * target it, so no migration is needed on membership changes. ++ */ ++ if (!priv->lag_bridge_ports[lag.id]) { ++ ret = MXL862XX_API_READ(priv, MXL862XX_BRIDGEPORT_ALLOC, ++ bp_alloc); ++ if (ret) { ++ NL_SET_ERR_MSG_MOD(extack, ++ "Failed to allocate LAG bridge port"); ++ return ret; ++ } ++ priv->lag_bridge_ports[lag.id] = ++ le16_to_cpu(bp_alloc.bridge_port_id); ++ } ++ ++ priv->ports[port].lag = dp->lag; ++ priv->ports[port].lag_tx_enabled = dp->lag_tx_enabled; ++ priv->ports[port].lag_hash_bits = hash_bits; ++ ++ /* Widen global hash to include this LAG's requirements */ ++ new_hash = priv->trunk_hash | hash_bits; ++ ret = mxl862xx_lag_set_hash(priv, new_hash); ++ if (ret) ++ goto err_undo; ++ ++ ret = mxl862xx_lag_sync(ds, dp->lag); ++ if (ret) ++ goto err_undo; ++ ++ return 0; ++ ++err_undo: ++ priv->ports[port].lag = NULL; ++ priv->ports[port].lag_tx_enabled = false; ++ priv->ports[port].lag_hash_bits = 0; ++ return ret; ++} ++ ++static int mxl862xx_port_lag_leave(struct dsa_switch *ds, int port, ++ const struct dsa_lag lag) ++{ ++ struct mxl862xx_bridge_port_alloc bp_alloc = {}; ++ struct mxl862xx_priv *priv = ds->priv; ++ u8 new_hash; ++ int ret; ++ ++ /* Restore this port's CTP to point to itself */ ++ ret = mxl862xx_lag_restore_ctp(priv, port); ++ if (ret) ++ dev_err(ds->dev, "failed to restore CTP for port %d: %pe\n", ++ port, ERR_PTR(ret)); ++ ++ priv->ports[port].lag = NULL; ++ priv->ports[port].lag_tx_enabled = false; ++ priv->ports[port].lag_hash_bits = 0; ++ ++ /* If other members remain, re-sync the LAG */ ++ if (mxl862xx_lag_master_port(ds, &lag) >= 0) { ++ ret = mxl862xx_lag_sync(ds, &lag); ++ if (ret) ++ dev_err(ds->dev, ++ "failed to re-sync LAG after port %d left: %pe\n", ++ port, ERR_PTR(ret)); ++ } else if (priv->lag_bridge_ports[lag.id]) { ++ /* Last member left -- disable P-mapper and free the ++ * LAG's dedicated bridge port. ++ */ ++ ret = mxl862xx_lag_disable_pmapper(ds, ++ priv->lag_bridge_ports[lag.id]); ++ if (ret) ++ dev_err(ds->dev, ++ "failed to disable P-mapper on LAG bridge port %u: %pe\n", ++ priv->lag_bridge_ports[lag.id], ERR_PTR(ret)); ++ ++ bp_alloc.bridge_port_id = ++ cpu_to_le16(priv->lag_bridge_ports[lag.id]); ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_FREE, ++ bp_alloc); ++ if (ret) ++ dev_err(ds->dev, ++ "failed to free LAG bridge port %u: %pe\n", ++ priv->lag_bridge_ports[lag.id], ERR_PTR(ret)); ++ ++ priv->lag_bridge_ports[lag.id] = 0; ++ } ++ ++ /* Recompute global hash from remaining LAGs */ ++ new_hash = mxl862xx_lag_recompute_hash(ds); ++ ret = mxl862xx_lag_set_hash(priv, new_hash); ++ if (ret) ++ dev_err(ds->dev, "failed to update trunk hash: %pe\n", ++ ERR_PTR(ret)); ++ ++ return 0; ++} ++ ++static int mxl862xx_port_lag_change(struct dsa_switch *ds, int port) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ ++ if (!priv->ports[port].lag) ++ return 0; ++ ++ priv->ports[port].lag_tx_enabled = dp->lag_tx_enabled; ++ ++ return mxl862xx_lag_sync(ds, priv->ports[port].lag); ++} ++ + static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port, + const struct dsa_bridge bridge, + bool *tx_fwd_offload, +@@ -1952,7 +2446,18 @@ static int mxl862xx_port_bridge_join(str + return 0; + } + +- return mxl862xx_sync_bridge_members(ds, &bridge); ++ ret = mxl862xx_sync_bridge_members(ds, &bridge); ++ if (ret) ++ return ret; ++ ++ /* If this port is in a LAG, re-sync the LAG bridge port so it ++ * picks up the new bridge_id (switching from standalone FID to ++ * the shared bridge FID). ++ */ ++ if (priv->ports[port].lag) ++ ret = mxl862xx_lag_sync(ds, priv->ports[port].lag); ++ ++ return ret; + } + + static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port, +@@ -2011,6 +2516,17 @@ static void mxl862xx_port_bridge_leave(s + "failed to update CPU VBP for port %d: %pe\n", port, + ERR_PTR(err)); + ++ /* If this port is in a LAG, re-sync the LAG bridge port so it ++ * reverts to the standalone FID. ++ */ ++ if (p->lag) { ++ err = mxl862xx_lag_sync(ds, p->lag); ++ if (err) ++ dev_err(ds->dev, ++ "failed to re-sync LAG after port %d left bridge: %pe\n", ++ port, ERR_PTR(err)); ++ } ++ + if (!dsa_bridge_ports(ds, bridge.dev)) + mxl862xx_free_bridge(ds, &bridge); + } +@@ -2636,18 +3152,17 @@ static int mxl862xx_get_fid(struct dsa_s + } + + /** +- * mxl862xx_fdb_bridge_port - Translate port for MAC table in tag_8021q mode ++ * mxl862xx_fdb_bridge_port - Translate port to effective bridge port ID + * @ds: DSA switch + * @port: port number passed by DSA (usually the CPU port for host entries) + * @db: database context identifying the user port or bridge + * +- * In tag_8021q mode, host FDB/MDB entries for standalone ports must use +- * the virtual bridge port (bridge_port_cpu) as the MAC table destination +- * so that known-unicast and known-multicast frames exit through the +- * virtual bridge port's egress EVLAN, which inserts the management VID. +- * Without this, the firmware forwards known traffic directly to the +- * physical CPU bridge port, bypassing management VID insertion, and DSA +- * drops the untagged frame. ++ * Returns the firmware bridge port ID that should be used for MAC table ++ * entries targeting @port: ++ * - CPU port in tag_8021q standalone mode: the virtual bridge port ++ * (bridge_port_cpu) so known traffic exits through egress EVLAN ++ * - User port in a LAG: the LAG's dedicated firmware bridge port ++ * - Otherwise: the port index itself + */ + static int mxl862xx_fdb_bridge_port(struct dsa_switch *ds, int port, + const struct dsa_db db) +@@ -2663,7 +3178,7 @@ static int mxl862xx_fdb_bridge_port(stru + return bp_cpu; + } + +- return port; ++ return mxl862xx_lag_bridge_port(priv, port); + } + + /** +@@ -2907,11 +3422,43 @@ static int mxl862xx_port_fdb_del(struct + return ret; + } + ++static int mxl862xx_lag_fdb_add(struct dsa_switch *ds, const struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ const struct dsa_db db) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ u16 lag_bp = priv->lag_bridge_ports[lag.id]; ++ int fid; ++ ++ if (!lag_bp) ++ return -ENOENT; ++ ++ fid = mxl862xx_get_fid(ds, db); ++ if (fid < 0) ++ return fid; ++ ++ return mxl862xx_fdb_add_per_fid(ds, addr, vid, fid, lag_bp); ++} ++ ++static int mxl862xx_lag_fdb_del(struct dsa_switch *ds, const struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ const struct dsa_db db) ++{ ++ int fid; ++ ++ fid = mxl862xx_get_fid(ds, db); ++ if (fid < 0) ++ return fid; ++ ++ return mxl862xx_fdb_del_per_fid(ds, addr, vid, fid); ++} ++ + static int mxl862xx_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) + { + struct mxl862xx_mac_table_read param = { .initial = 1 }; + struct mxl862xx_priv *priv = ds->priv; ++ u16 lag_bp = mxl862xx_lag_bridge_port(priv, port); + u32 entry_port_id; + int ret; + +@@ -2925,7 +3472,7 @@ static int mxl862xx_port_fdb_dump(struct + + entry_port_id = le32_to_cpu(param.port_id); + +- if (entry_port_id == port) { ++ if (entry_port_id == port || entry_port_id == lag_bp) { + ret = cb(param.mac, FIELD_GET(MXL862XX_TCI_VLAN_ID, + le16_to_cpu(param.tci)), + param.static_entry, data); +@@ -3562,6 +4109,11 @@ static const struct dsa_switch_ops mxl86 + .port_fdb_dump = mxl862xx_port_fdb_dump, + .port_mdb_add = mxl862xx_port_mdb_add, + .port_mdb_del = mxl862xx_port_mdb_del, ++ .port_lag_join = mxl862xx_port_lag_join, ++ .port_lag_leave = mxl862xx_port_lag_leave, ++ .port_lag_change = mxl862xx_port_lag_change, ++ .lag_fdb_add = mxl862xx_lag_fdb_add, ++ .lag_fdb_del = mxl862xx_lag_fdb_del, + .port_vlan_filtering = mxl862xx_port_vlan_filtering, + .port_vlan_add = mxl862xx_port_vlan_add, + .port_vlan_del = mxl862xx_port_vlan_del, +@@ -3602,6 +4154,7 @@ static int mxl862xx_probe(struct mdio_de + ds->num_ports = MXL862XX_MAX_PORTS; + ds->fdb_isolation = true; + ds->max_num_bridges = MXL862XX_MAX_BRIDGES; ++ ds->num_lag_ids = MXL862XX_MAX_LAG_IDS; + + mxl862xx_host_init(priv); + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -14,6 +14,19 @@ + #define MXL862XX_MAX_BRIDGE_PORTS 128 + #define MXL862XX_TOTAL_EVLAN_ENTRIES 1024 + #define MXL862XX_TOTAL_VF_ENTRIES 1024 ++#define MXL862XX_MAX_LAG_IDS 16 ++ ++/* Trunk hash field bitmask (matches PCE_TRUNK_CONF layout) */ ++#define MXL862XX_TRUNK_HASH_SA BIT(0) ++#define MXL862XX_TRUNK_HASH_DA BIT(1) ++#define MXL862XX_TRUNK_HASH_SIP BIT(2) ++#define MXL862XX_TRUNK_HASH_DIP BIT(3) ++#define MXL862XX_TRUNK_HASH_SPORT BIT(4) ++#define MXL862XX_TRUNK_HASH_DPORT BIT(5) ++ ++/* P-mapper LAG entries occupy indices 9..72 (64 entries) */ ++#define MXL862XX_PMAPPER_LAG_FIRST 9 ++#define MXL862XX_PMAPPER_LAG_COUNT 64 + + /* Number of __le16 words in a firmware portmap (128-bit bitmap). */ + #define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16) +@@ -228,6 +241,12 @@ struct mxl862xx_port_stats { + * @stats_lock: protects accumulator reads in .get_stats64 against + * concurrent updates from the polling work + * @tag_8021q_vid: currently assigned tag_8021q management VID ++ * @lag: non-NULL when port is member of a LAG group; ++ * points to the DSA LAG structure ++ * @lag_tx_enabled: true when this port is active for TX in its LAG ++ * @lag_hash_bits: hash field bitmask (MXL862XX_TRUNK_HASH_*) requested ++ * when this port joined its LAG; used to recompute the ++ * global trunk_hash when a LAG is destroyed + */ + struct mxl862xx_port { + struct mxl862xx_priv *priv; +@@ -250,6 +269,10 @@ struct mxl862xx_port { + struct work_struct host_flood_work; + u16 tag_8021q_vid; + struct mxl862xx_evlan_block cpu_egress_evlan; ++ /* LAG state */ ++ struct dsa_lag *lag; ++ bool lag_tx_enabled; ++ u8 lag_hash_bits; + /* Hardware stats accumulation */ + struct mxl862xx_port_stats stats; + spinlock_t stats_lock; +@@ -328,6 +351,15 @@ union mxl862xx_fw_version { + * DSA bridge number. Indexed by dsa_bridge.num + * (0 .. ds->max_num_bridges). + * @vf_block_size: per-port VLAN Filter block size ++ * @lag_bridge_ports: maps DSA LAG ID to firmware bridge port ID; ++ * zero means no bridge port allocated for that LAG. ++ * Indexed by lag->id (entry 0 is unused). ++ * The bridge port is stable for the LAG's lifetime ++ * so FDB/MDB entries never need migration on ++ * membership changes. ++ * @trunk_hash: current global hash field bitmask (6 bits, ++ * MXL862XX_TRUNK_HASH_*); union of all active LAGs' ++ * hash requirements + * @stats_work: periodic work item that polls RMON hardware counters + * and accumulates them into 64-bit per-port stats + */ +@@ -346,6 +378,8 @@ struct mxl862xx_priv { + u16 evlan_egress_size; + u16 cpu_evlan_ingress_size; + u16 vf_block_size; ++ u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1]; ++ u8 trunk_hash; + struct delayed_work stats_work; + }; + diff --git a/target/linux/generic/pending-6.12/760-18-net-dsa-mxl862xx-add-support-for-mirror-port.patch b/target/linux/generic/pending-6.12/760-18-net-dsa-mxl862xx-add-support-for-mirror-port.patch new file mode 100644 index 00000000000..faf69c0c0ea --- /dev/null +++ b/target/linux/generic/pending-6.12/760-18-net-dsa-mxl862xx-add-support-for-mirror-port.patch @@ -0,0 +1,229 @@ +From fbfa1b0649c578e0d43e3a61617b53a9a722efad Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 12:05:29 +0000 +Subject: [PATCH 27/35] net: dsa: mxl862xx: add support for mirror port + +The MxL862xx hardware supports a single monitor port which can be +configured to mirror any other port's ingress and/or egress traffic. + +Implement support for .port_mirror_add/.port_mirror_del using this +feature. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 12 +++ + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 1 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 118 ++++++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx.h | 8 ++ + 4 files changed, 139 insertions(+) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -729,6 +729,18 @@ struct mxl862xx_bridge_port_config { + } __packed; + + /** ++ * struct mxl862xx_monitor_port_cfg - Monitor port configuration ++ * @port_id: Destination port for mirrored traffic (zero-based) ++ * @sub_if_id: Monitoring sub-interface ID ++ * @monitor_port: Reserved ++ */ ++struct mxl862xx_monitor_port_cfg { ++ u8 port_id; ++ __le16 sub_if_id; ++ u8 monitor_port; ++} __packed; ++ ++/** + * struct mxl862xx_cfg - Global Switch configuration Attributes + * @mac_table_age_timer: See &enum mxl862xx_age_timer + * @age_timer: Custom MAC table aging timer in seconds +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -30,6 +30,7 @@ + #define MXL862XX_COMMON_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x7) + #define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9) + #define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa) ++#define MXL862XX_COMMON_MONITORPORTCFGSET (MXL862XX_COMMON_MAGIC + 0xe) + #define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11) + + #define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2) +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -1129,6 +1129,8 @@ static int mxl862xx_setup(struct dsa_swi + (n_user_ports + n_cpu_ports); + } + ++ priv->mirror_dest = -1; ++ + ret = mxl862xx_setup_drop_meter(ds); + if (ret) + return ret; +@@ -2018,6 +2020,120 @@ static int mxl862xx_setup_cpu_bridge(str + return mxl862xx_set_bridge_port(ds, port); + } + ++static int mxl862xx_port_mirror_add(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror, ++ bool ingress, ++ struct netlink_ext_ack *extack) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ struct mxl862xx_monitor_port_cfg mon = { ++ .port_id = mirror->to_local_port, ++ }; ++ struct mxl862xx_ctp_port_config ctp = { ++ .logical_port_id = port, ++ .mask = cpu_to_le32( ++ MXL862XX_CTP_PORT_CONFIG_MASK_LOOPBACK_AND_MIRROR), ++ .ingress_mirror_enable = p->ingress_mirror, ++ .egress_mirror_enable = p->egress_mirror, ++ }; ++ int ret; ++ ++ /* The hardware has a single global monitor port. Reject if an ++ * existing mirror session targets a different destination. ++ */ ++ if (priv->mirror_dest >= 0 && ++ priv->mirror_dest != mirror->to_local_port) { ++ NL_SET_ERR_MSG_MOD(extack, ++ "Only one mirror destination port is supported"); ++ return -EBUSY; ++ } ++ ++ if (ingress) ++ ctp.ingress_mirror_enable = 1; ++ else ++ ctp.egress_mirror_enable = 1; ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_CTP_PORTCONFIGSET, ctp); ++ if (ret) { ++ dev_err(ds->dev, "mirror: CTP write failed for port %d: %pe\n", ++ port, ERR_PTR(ret)); ++ return ret; ++ } ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_COMMON_MONITORPORTCFGSET, mon); ++ if (ret) { ++ dev_err(ds->dev, ++ "mirror: failed to set monitor port %d: %pe\n", ++ mirror->to_local_port, ERR_PTR(ret)); ++ /* Roll back CTP change */ ++ ctp.ingress_mirror_enable = p->ingress_mirror; ++ ctp.egress_mirror_enable = p->egress_mirror; ++ MXL862XX_API_WRITE(priv, MXL862XX_CTP_PORTCONFIGSET, ctp); ++ return ret; ++ } ++ ++ if (ingress) ++ p->ingress_mirror = true; ++ else ++ p->egress_mirror = true; ++ ++ priv->mirror_dest = mirror->to_local_port; ++ ++ return 0; ++} ++ ++static void mxl862xx_port_mirror_del(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_port *p = &priv->ports[port]; ++ struct mxl862xx_ctp_port_config ctp = { ++ .logical_port_id = port, ++ .mask = cpu_to_le32( ++ MXL862XX_CTP_PORT_CONFIG_MASK_LOOPBACK_AND_MIRROR), ++ .ingress_mirror_enable = p->ingress_mirror, ++ .egress_mirror_enable = p->egress_mirror, ++ }; ++ struct mxl862xx_monitor_port_cfg mon = {}; ++ bool active = false; ++ int i, ret; ++ ++ if (mirror->ingress) ++ ctp.ingress_mirror_enable = 0; ++ else ++ ctp.egress_mirror_enable = 0; ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_CTP_PORTCONFIGSET, ctp); ++ if (ret) ++ dev_err(ds->dev, "mirror: CTP write failed for port %d: %pe\n", ++ port, ERR_PTR(ret)); ++ ++ if (mirror->ingress) ++ p->ingress_mirror = false; ++ else ++ p->egress_mirror = false; ++ ++ /* If no ports have any mirrors active, clear the monitor port */ ++ for (i = 0; i < ds->num_ports; i++) { ++ if (priv->ports[i].ingress_mirror || ++ priv->ports[i].egress_mirror) { ++ active = true; ++ break; ++ } ++ } ++ ++ if (active) ++ return; ++ ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_COMMON_MONITORPORTCFGSET, mon); ++ if (ret) ++ dev_err(ds->dev, "mirror: failed to clear monitor port: %pe\n", ++ ERR_PTR(ret)); ++ ++ priv->mirror_dest = -1; ++} ++ + /** + * mxl862xx_lag_master_port - Find the LAG master (lowest-numbered member) + * @ds: DSA switch +@@ -4109,6 +4225,8 @@ static const struct dsa_switch_ops mxl86 + .port_fdb_dump = mxl862xx_port_fdb_dump, + .port_mdb_add = mxl862xx_port_mdb_add, + .port_mdb_del = mxl862xx_port_mdb_del, ++ .port_mirror_add = mxl862xx_port_mirror_add, ++ .port_mirror_del = mxl862xx_port_mirror_del, + .port_lag_join = mxl862xx_port_lag_join, + .port_lag_leave = mxl862xx_port_lag_leave, + .port_lag_change = mxl862xx_port_lag_change, +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -241,6 +241,8 @@ struct mxl862xx_port_stats { + * @stats_lock: protects accumulator reads in .get_stats64 against + * concurrent updates from the polling work + * @tag_8021q_vid: currently assigned tag_8021q management VID ++ * @ingress_mirror: true when ingress mirroring is active on this port ++ * @egress_mirror: true when egress mirroring is active on this port + * @lag: non-NULL when port is member of a LAG group; + * points to the DSA LAG structure + * @lag_tx_enabled: true when this port is active for TX in its LAG +@@ -269,6 +271,9 @@ struct mxl862xx_port { + struct work_struct host_flood_work; + u16 tag_8021q_vid; + struct mxl862xx_evlan_block cpu_egress_evlan; ++ /* Mirror state */ ++ bool ingress_mirror; ++ bool egress_mirror; + /* LAG state */ + struct dsa_lag *lag; + bool lag_tx_enabled; +@@ -360,6 +365,8 @@ union mxl862xx_fw_version { + * @trunk_hash: current global hash field bitmask (6 bits, + * MXL862XX_TRUNK_HASH_*); union of all active LAGs' + * hash requirements ++ * @mirror_dest: current mirror destination port, or -1 if no mirror ++ * session is active; used to detect monitor port conflicts + * @stats_work: periodic work item that polls RMON hardware counters + * and accumulates them into 64-bit per-port stats + */ +@@ -380,6 +387,7 @@ struct mxl862xx_priv { + u16 vf_block_size; + u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1]; + u8 trunk_hash; ++ int mirror_dest; + struct delayed_work stats_work; + }; + diff --git a/target/linux/generic/pending-6.12/760-19-net-dsa-wire-flash_update-devlink-callback-to-driver.patch b/target/linux/generic/pending-6.12/760-19-net-dsa-wire-flash_update-devlink-callback-to-driver.patch new file mode 100644 index 00000000000..19da9a0010a --- /dev/null +++ b/target/linux/generic/pending-6.12/760-19-net-dsa-wire-flash_update-devlink-callback-to-driver.patch @@ -0,0 +1,57 @@ +From 67f82834819b71417b58dc1293c20f71b990264f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 16:30:08 +0000 +Subject: [PATCH 28/35] net: dsa: wire flash_update devlink callback to drivers + +Add a devlink_flash_update callback to dsa_switch_ops so that DSA +drivers can support devlink dev flash without open-coding the devlink +plumbing. The new trampoline in net/dsa/devlink.c follows the existing +dsa_devlink_info_get pattern exactly. + +Signed-off-by: Daniel Golle +--- + include/net/dsa.h | 3 +++ + net/dsa/devlink.c | 13 +++++++++++++ + 2 files changed, 16 insertions(+) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -1185,6 +1185,9 @@ struct dsa_switch_ops { + int (*devlink_info_get)(struct dsa_switch *ds, + struct devlink_info_req *req, + struct netlink_ext_ack *extack); ++ int (*devlink_flash_update)(struct dsa_switch *ds, ++ struct devlink_flash_update_params *params, ++ struct netlink_ext_ack *extack); + int (*devlink_sb_pool_get)(struct dsa_switch *ds, + unsigned int sb_index, u16 pool_index, + struct devlink_sb_pool_info *pool_info); +--- a/net/dsa/devlink.c ++++ b/net/dsa/devlink.c +@@ -20,6 +20,18 @@ static int dsa_devlink_info_get(struct d + return -EOPNOTSUPP; + } + ++static int dsa_devlink_flash_update(struct devlink *dl, ++ struct devlink_flash_update_params *params, ++ struct netlink_ext_ack *extack) ++{ ++ struct dsa_switch *ds = dsa_devlink_to_ds(dl); ++ ++ if (!ds->ops->devlink_flash_update) ++ return -EOPNOTSUPP; ++ ++ return ds->ops->devlink_flash_update(ds, params, extack); ++} ++ + static int dsa_devlink_sb_pool_get(struct devlink *dl, + unsigned int sb_index, u16 pool_index, + struct devlink_sb_pool_info *pool_info) +@@ -169,6 +181,7 @@ dsa_devlink_sb_occ_tc_port_bind_get(stru + + static const struct devlink_ops dsa_devlink_ops = { + .info_get = dsa_devlink_info_get, ++ .flash_update = dsa_devlink_flash_update, + .sb_pool_get = dsa_devlink_sb_pool_get, + .sb_pool_set = dsa_devlink_sb_pool_set, + .sb_port_pool_get = dsa_devlink_sb_port_pool_get, diff --git a/target/linux/generic/pending-6.12/760-20-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch b/target/linux/generic/pending-6.12/760-20-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch new file mode 100644 index 00000000000..17668b9734a --- /dev/null +++ b/target/linux/generic/pending-6.12/760-20-net-dsa-mxl862xx-add-SMDIO-clause-22-register-access.patch @@ -0,0 +1,72 @@ +From 1a87b829ef3280d646dc480f7b261d9e32896899 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 16:30:17 +0000 +Subject: [PATCH 29/35] net: dsa: mxl862xx: add SMDIO clause-22 register access + +Add mxl862xx_smdio_read() and mxl862xx_smdio_write() for clause-22 +SMDIO register access. MCUboot rescue mode only exposes clause-22 +registers; the existing clause-45 MMD interface is unavailable during +firmware transfer. The MDIO bus lock is held per-transaction (not +across polls) so that SB PDI polling during flash erase does not +starve other MDIO users. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-host.c | 35 ++++++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-host.h | 2 ++ + 2 files changed, 37 insertions(+) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c +@@ -493,6 +493,41 @@ out: + return ret; + } + ++#define MXL862XX_SMDIO_ADDR_REG 0x1f ++#define MXL862XX_SMDIO_PAGE_MASK 0xfff0 ++#define MXL862XX_SMDIO_OFF_MASK 0x000f ++ ++int mxl862xx_smdio_read(struct mxl862xx_priv *priv, u32 addr) ++{ ++ struct mii_bus *bus = priv->mdiodev->bus; ++ int phy = priv->mdiodev->addr; ++ int ret; ++ ++ mutex_lock(&bus->mdio_lock); ++ ret = __mdiobus_write(bus, phy, MXL862XX_SMDIO_ADDR_REG, ++ addr & MXL862XX_SMDIO_PAGE_MASK); ++ if (ret >= 0) ++ ret = __mdiobus_read(bus, phy, addr & MXL862XX_SMDIO_OFF_MASK); ++ mutex_unlock(&bus->mdio_lock); ++ return ret; ++} ++ ++int mxl862xx_smdio_write(struct mxl862xx_priv *priv, u32 addr, u16 val) ++{ ++ struct mii_bus *bus = priv->mdiodev->bus; ++ int phy = priv->mdiodev->addr; ++ int ret; ++ ++ mutex_lock(&bus->mdio_lock); ++ ret = __mdiobus_write(bus, phy, MXL862XX_SMDIO_ADDR_REG, ++ addr & MXL862XX_SMDIO_PAGE_MASK); ++ if (ret >= 0) ++ ret = __mdiobus_write(bus, phy, addr & MXL862XX_SMDIO_OFF_MASK, ++ val); ++ mutex_unlock(&bus->mdio_lock); ++ return ret; ++} ++ + void mxl862xx_host_init(struct mxl862xx_priv *priv) + { + INIT_WORK(&priv->crc_err_work, mxl862xx_crc_err_work_fn); +--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.h +@@ -18,5 +18,7 @@ int mxl862xx_api_wrap(struct mxl862xx_pr + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) + + int mxl862xx_reset(struct mxl862xx_priv *priv); ++int mxl862xx_smdio_read(struct mxl862xx_priv *priv, u32 addr); ++int mxl862xx_smdio_write(struct mxl862xx_priv *priv, u32 addr, u16 val); + + #endif /* __MXL862XX_HOST_H */ diff --git a/target/linux/generic/pending-6.12/760-21-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch b/target/linux/generic/pending-6.12/760-21-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch new file mode 100644 index 00000000000..28b91f8021c --- /dev/null +++ b/target/linux/generic/pending-6.12/760-21-net-dsa-mxl862xx-add-devlink-flash_update-and-info_g.patch @@ -0,0 +1,569 @@ +From b7e8f8fd4493b255f0f01fe790a73ad61b5e8ce8 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 16:30:31 +0000 +Subject: [PATCH 30/35] net: dsa: mxl862xx: add devlink flash_update and + info_get + +Implement runtime firmware upgrade via "devlink dev flash" and version +reporting via "devlink dev info": + + devlink dev info mdio_bus// + devlink dev flash mdio_bus// file + +The driver sends SYS_MISC_FW_UPDATE to enter MCUboot rescue mode, +transfers the signed image over the SB PDI bulk-transfer protocol +(clause-22 SMDIO), waits for the switch to reboot, then schedules +device_reprobe() for a clean remove()+probe() cycle. + +Before the transfer begins the driver closes all conduit interfaces +and marks every netdev (user and conduit) not-present via +netif_device_detach() so that userspace cannot bring ports back up +during the ~15 minute flash process. Progress is reported through +devlink status notifications. Once the FW_UPDATE command has been +sent the switch is in MCUboot mode and normal operation can only be +restored by a reprobe, so the driver always schedules one regardless +of transfer outcome. + +The reprobe work item is dynamically allocated (following the iwlwifi +pattern) because device_reprobe() triggers remove() which frees the +devm-managed priv while the work is still executing. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/Makefile | 2 +- + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 1 + + drivers/net/dsa/mxl862xx/mxl862xx-fw.c | 434 +++++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx-fw.h | 15 + + drivers/net/dsa/mxl862xx/mxl862xx-host.c | 7 + + drivers/net/dsa/mxl862xx/mxl862xx.c | 4 + + drivers/net/dsa/mxl862xx/mxl862xx.h | 2 + + 7 files changed, 464 insertions(+), 1 deletion(-) + create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-fw.c + create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-fw.h + +--- a/drivers/net/dsa/mxl862xx/Makefile ++++ b/drivers/net/dsa/mxl862xx/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o +-mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o mxl862xx-phylink.o ++mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o mxl862xx-phylink.o mxl862xx-fw.o +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -83,6 +83,7 @@ + #define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x1) + #define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x2) + ++#define SYS_MISC_FW_UPDATE (SYS_MISC_MAGIC + 0x1) + #define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2) + + #define MXL862XX_XPCS_MAGIC 0x1a00 +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-fw.c +@@ -0,0 +1,434 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Firmware flash and devlink support for MaxLinear MxL862xx ++ * ++ * Copyright (C) 2025 Daniel Golle ++ * ++ * Usage: ++ * # Query running firmware version: ++ * devlink dev info mdio_bus// ++ * ++ * # Flash new firmware (all ports are taken down automatically): ++ * devlink dev flash mdio_bus// file ++ * ++ * The flash process takes approximately 15 minutes. Progress is ++ * reported via devlink status notifications. After a successful (or ++ * failed) flash the driver reprobes the device automatically. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mxl862xx.h" ++#include "mxl862xx-api.h" ++#include "mxl862xx-cmd.h" ++#include "mxl862xx-fw.h" ++#include "mxl862xx-host.h" ++ ++/* SB PDI registers (clause-22 SMDIO address space) */ ++#define MXL862XX_SB_PDI_CTRL 0xe100 ++#define MXL862XX_SB_PDI_ADDR 0xe101 ++#define MXL862XX_SB_PDI_DATA 0xe102 ++#define MXL862XX_SB_PDI_STAT 0xe103 ++ ++/* SB PDI CTRL modes */ ++#define MXL862XX_SB_PDI_CTRL_RST 0x00 ++#define MXL862XX_SB_PDI_CTRL_WR 0x02 ++ ++/* SB PDI handshake magic */ ++#define MXL862XX_SB_PDI_READY 0xc55c ++#define MXL862XX_SB_PDI_START 0xf48f ++#define MXL862XX_SB_PDI_END 0x3cc3 ++ ++/* Firmware transfer geometry */ ++#define MXL862XX_FW_HDR_SIZE 20 ++#define MXL862XX_FW_BANK_HALF 16384 /* words per half-bank */ ++#define MXL862XX_FW_BANK_SLICE 32760 /* words per full slice */ ++#define MXL862XX_FW_SB1_ADDR 0x7800 /* SB1 word address */ ++ ++/* Timeouts */ ++#define MXL862XX_FW_READY_TIMEOUT_MS 30000 ++#define MXL862XX_FW_ACK_TIMEOUT_MS 5000 ++#define MXL862XX_FW_ERASE_TIMEOUT_MS 300000 /* flash erase is very slow */ ++#define MXL862XX_FW_WRITE_TIMEOUT_MS 120000 /* per-slice program timeout */ ++#define MXL862XX_FW_REBOOT_DELAY_MS 5000 ++ ++static void mxl862xx_sb_pdi_reset(struct mxl862xx_priv *priv) ++{ ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL, ++ MXL862XX_SB_PDI_CTRL_RST); ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_ADDR, ++ MXL862XX_SB_PDI_CTRL_RST); ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_DATA, ++ MXL862XX_SB_PDI_CTRL_RST); ++} ++ ++static int mxl862xx_sb_pdi_poll_stat(struct mxl862xx_priv *priv, u16 expected, ++ unsigned long timeout_ms) ++{ ++ unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); ++ int ret; ++ ++ do { ++ ret = mxl862xx_smdio_read(priv, MXL862XX_SB_PDI_STAT); ++ if (ret < 0) ++ return ret; ++ if ((u16)ret == expected) ++ return 0; ++ usleep_range(10000, 11000); ++ } while (time_before(jiffies, timeout)); ++ ++ return -ETIMEDOUT; ++} ++ ++/* Reprobe work -- dynamically allocated so it survives remove(). ++ * device_reprobe() -> remove() frees priv (devm) while work is executing, ++ * so the work struct must not live in mxl862xx_priv. ++ */ ++struct mxl862xx_reprobe { ++ struct device *dev; ++ struct delayed_work dwork; ++}; ++ ++static void mxl862xx_reprobe_work_fn(struct work_struct *work) ++{ ++ struct mxl862xx_reprobe *reprobe = ++ container_of(work, struct mxl862xx_reprobe, dwork.work); ++ ++ if (device_reprobe(reprobe->dev)) ++ dev_err(reprobe->dev, "reprobe failed\n"); ++ put_device(reprobe->dev); ++ kfree(reprobe); ++ module_put(THIS_MODULE); ++} ++ ++/* MCUboot firmware image header (20 bytes) */ ++struct mxl862xx_fw_hdr { ++ __le32 image_type; ++ __le32 image_size_1; ++ __le32 image_checksum_1; ++ __le32 image_size_2; ++ __le32 image_checksum_2; ++} __packed; ++ ++static int mxl862xx_flash_firmware(struct mxl862xx_priv *priv, ++ const struct firmware *fw, ++ struct devlink *dl) ++{ ++ const struct mxl862xx_fw_hdr *hdr; ++ u32 word_idx = 0, data_written = 0, idx = 0; ++ unsigned long next_notify = 0; ++ const u8 *payload; ++ u32 payload_size; ++ u16 word, fdata; ++ int ret, i; ++ u32 crc; ++ ++ if (fw->size < MXL862XX_FW_HDR_SIZE) ++ return -EINVAL; ++ ++ hdr = (const struct mxl862xx_fw_hdr *)fw->data; ++ payload = fw->data + MXL862XX_FW_HDR_SIZE; ++ payload_size = le32_to_cpu(hdr->image_size_1) + ++ le32_to_cpu(hdr->image_size_2); ++ ++ if (payload_size > fw->size - MXL862XX_FW_HDR_SIZE) { ++ dev_err(&priv->mdiodev->dev, ++ "flash: firmware file too small for declared size\n"); ++ return -EINVAL; ++ } ++ ++ /* Validate CRC-32 of both image slots before touching hardware */ ++ if (le32_to_cpu(hdr->image_size_1)) { ++ crc = ~crc32_le(~0U, payload, ++ le32_to_cpu(hdr->image_size_1)); ++ if (crc != le32_to_cpu(hdr->image_checksum_1)) { ++ dev_err(&priv->mdiodev->dev, ++ "flash: image 1 CRC mismatch (got %08x, expected %08x)\n", ++ crc, le32_to_cpu(hdr->image_checksum_1)); ++ return -EINVAL; ++ } ++ } ++ ++ if (le32_to_cpu(hdr->image_size_2)) { ++ crc = ~crc32_le(~0U, ++ payload + le32_to_cpu(hdr->image_size_1), ++ le32_to_cpu(hdr->image_size_2)); ++ if (crc != le32_to_cpu(hdr->image_checksum_2)) { ++ dev_err(&priv->mdiodev->dev, ++ "flash: image 2 CRC mismatch (got %08x, expected %08x)\n", ++ crc, le32_to_cpu(hdr->image_checksum_2)); ++ return -EINVAL; ++ } ++ } ++ ++ /* Step 1: Tell firmware to enter MCUboot rescue mode. ++ * The FW_UPDATE command takes no payload (size 0). ++ */ ++ ret = mxl862xx_api_wrap(priv, SYS_MISC_FW_UPDATE, NULL, 0, ++ false, false); ++ if (ret) { ++ dev_err(&priv->mdiodev->dev, ++ "flash: FW_UPDATE command failed: %pe\n", ++ ERR_PTR(ret)); ++ return ret; ++ } ++ ++ /* From this point on, the switch is in MCUboot rescue mode. ++ * Any failure must go through the end_magic label to tell ++ * MCUboot to reboot rather than leaving it stuck waiting. ++ */ ++ ++ /* Step 2: Reset PDI and wait for bootloader ready */ ++ devlink_flash_update_status_notify(dl, "Waiting for bootloader", ++ NULL, 0, 0); ++ mxl862xx_sb_pdi_reset(priv); ++ ret = mxl862xx_sb_pdi_poll_stat(priv, MXL862XX_SB_PDI_READY, ++ MXL862XX_FW_READY_TIMEOUT_MS); ++ if (ret) { ++ dev_err(&priv->mdiodev->dev, ++ "flash: bootloader not ready: %pe\n", ERR_PTR(ret)); ++ goto end_magic; ++ } ++ ++ /* Step 3: Start handshake */ ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_STAT, ++ MXL862XX_SB_PDI_START); ++ ret = mxl862xx_sb_pdi_poll_stat(priv, MXL862XX_SB_PDI_START + 1, ++ MXL862XX_FW_ACK_TIMEOUT_MS); ++ if (ret) { ++ dev_err(&priv->mdiodev->dev, ++ "flash: start handshake failed: %pe\n", ERR_PTR(ret)); ++ goto end_magic; ++ } ++ ++ /* Step 4: Transfer 20-byte header using auto-increment write mode */ ++ devlink_flash_update_status_notify(dl, "Erasing flash", NULL, 0, 0); ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL, ++ MXL862XX_SB_PDI_CTRL_WR); ++ for (i = 0; i < MXL862XX_FW_HDR_SIZE / 2; i++) { ++ word = fw->data[i * 2] | ++ ((u16)fw->data[i * 2 + 1] << 8); ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_DATA, word); ++ } ++ mxl862xx_sb_pdi_reset(priv); ++ ++ /* Write header byte count to STAT to trigger erase */ ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_STAT, ++ MXL862XX_FW_HDR_SIZE); ++ ++ /* Wait for header ACK (header_size + 1) */ ++ ret = mxl862xx_sb_pdi_poll_stat(priv, MXL862XX_FW_HDR_SIZE + 1, ++ MXL862XX_FW_ACK_TIMEOUT_MS); ++ if (ret) { ++ dev_err(&priv->mdiodev->dev, ++ "flash: header ACK failed: %pe\n", ERR_PTR(ret)); ++ goto end_magic; ++ } ++ ++ /* Step 5: Wait for erase to complete (STAT goes to 0) */ ++ ret = mxl862xx_sb_pdi_poll_stat(priv, 0, ++ MXL862XX_FW_ERASE_TIMEOUT_MS); ++ if (ret) { ++ dev_err(&priv->mdiodev->dev, ++ "flash: erase timeout: %pe\n", ERR_PTR(ret)); ++ goto end_magic; ++ } ++ ++ /* Step 6: Transfer payload using dual-bank auto-increment writes */ ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL, ++ MXL862XX_SB_PDI_CTRL_WR); ++ ++ while (idx < payload_size) { ++ if (idx + 1 < payload_size) { ++ fdata = payload[idx] | ++ ((u16)payload[idx + 1] << 8); ++ idx += 2; ++ data_written += 2; ++ } else { ++ fdata = payload[idx]; ++ idx++; ++ data_written++; ++ } ++ ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_DATA, fdata); ++ word_idx++; ++ ++ /* Last byte(s): flush final partial slice */ ++ if (idx >= payload_size) { ++ mxl862xx_sb_pdi_reset(priv); ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_STAT, ++ data_written); ++ break; ++ } ++ ++ /* Half-bank boundary: switch to SB1 address */ ++ if (word_idx == MXL862XX_FW_BANK_HALF) { ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL, ++ MXL862XX_SB_PDI_CTRL_RST); ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_ADDR, ++ MXL862XX_FW_SB1_ADDR); ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL, ++ MXL862XX_SB_PDI_CTRL_WR); ++ } else if (word_idx >= MXL862XX_FW_BANK_SLICE) { ++ /* Full slice: flush and wait for program */ ++ mxl862xx_sb_pdi_reset(priv); ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_STAT, ++ data_written); ++ word_idx = 0; ++ data_written = 0; ++ ++ ret = mxl862xx_sb_pdi_poll_stat( ++ priv, 0, MXL862XX_FW_WRITE_TIMEOUT_MS); ++ if (ret) { ++ dev_err(&priv->mdiodev->dev, ++ "flash: write timeout at %u/%u: %pe\n", ++ idx, payload_size, ERR_PTR(ret)); ++ goto end_magic; ++ } ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_CTRL, ++ MXL862XX_SB_PDI_CTRL_WR); ++ ++ if (time_after(jiffies, next_notify)) { ++ devlink_flash_update_status_notify( ++ dl, "Flashing", NULL, ++ idx, payload_size); ++ next_notify = jiffies + ++ msecs_to_jiffies(500); ++ } ++ } ++ } ++ ++ /* Wait for final slice to be programmed */ ++ ret = mxl862xx_sb_pdi_poll_stat(priv, 0, ++ MXL862XX_FW_WRITE_TIMEOUT_MS); ++ if (ret) { ++ dev_err(&priv->mdiodev->dev, ++ "flash: final write timeout: %pe\n", ERR_PTR(ret)); ++ goto end_magic; ++ } ++ ++ devlink_flash_update_status_notify(dl, "Flashing", NULL, ++ payload_size, payload_size); ++ ++end_magic: ++ /* Always send end magic so MCUboot reboots instead of sitting ++ * idle. The hardware reset during reprobe recovers the switch ++ * regardless of whether the transfer succeeded or failed. ++ */ ++ mxl862xx_smdio_write(priv, MXL862XX_SB_PDI_STAT, ++ MXL862XX_SB_PDI_END); ++ msleep(MXL862XX_FW_REBOOT_DELAY_MS); ++ ++ return ret; ++} ++ ++int mxl862xx_devlink_info_get(struct dsa_switch *ds, ++ struct devlink_info_req *req, ++ struct netlink_ext_ack *extack) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ char ver_str[32]; ++ ++ snprintf(ver_str, sizeof(ver_str), "%u.%u.%u", ++ priv->fw_version.major, priv->fw_version.minor, ++ priv->fw_version.revision); ++ ++ return devlink_info_version_running_put(req, "fw", ver_str); ++} ++ ++int mxl862xx_devlink_flash_update(struct dsa_switch *ds, ++ struct devlink_flash_update_params *params, ++ struct netlink_ext_ack *extack) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_sys_fw_image_version ver = {}; ++ struct mxl862xx_reprobe *reprobe; ++ struct dsa_port *dp; ++ int ret, i; ++ ++ if (params->component) { ++ NL_SET_ERR_MSG_MOD(extack, "component is not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ dev_info(ds->dev, "flash: running firmware %u.%u.%u\n", ++ priv->fw_version.major, priv->fw_version.minor, ++ priv->fw_version.revision); ++ ++ /* Close all user and CPU ports while the firmware is still ++ * alive. dev_close() on user ports triggers multicast group ++ * leave and host MDB/FDB removal on the CPU port through the ++ * normal DSA callbacks so the core's tracking lists are ++ * drained before we enter MCUboot. Then mark user ports ++ * not-present so userspace cannot bring them back up during ++ * the (slow) flash process. The conduit is only closed, not ++ * detached -- it is owned by the Ethernet MAC driver and ++ * dev_open() during reprobe must be able to bring it back. ++ */ ++ rtnl_lock(); ++ dsa_switch_for_each_user_port(dp, ds) { ++ if (dp->user) { ++ dev_close(dp->user); ++ netif_device_detach(dp->user); ++ } ++ } ++ dsa_switch_for_each_cpu_port(dp, ds) ++ dev_close(dp->conduit); ++ rtnl_unlock(); ++ ++ /* Block all firmware API commands while the switch is being ++ * reflashed. The conduit is intentionally kept open -- it is ++ * owned by the Ethernet MAC driver and would not recover on ++ * reprobe if we closed it here. ++ */ ++ priv->block_host = true; ++ ++ /* Stop stats polling and pending host-flood work */ ++ cancel_delayed_work_sync(&priv->stats_work); ++ for (i = 0; i < ds->num_ports; i++) ++ cancel_work_sync(&priv->ports[i].host_flood_work); ++ ++ ret = mxl862xx_flash_firmware(priv, params->fw, ds->devlink); ++ if (ret) ++ NL_SET_ERR_MSG_MOD(extack, "firmware transfer failed"); ++ ++ if (!ret) { ++ /* Read new firmware version (switch just rebooted). ++ * Temporarily lift the block for this single query. ++ */ ++ priv->block_host = false; ++ memset(&ver, 0, sizeof(ver)); ++ if (!MXL862XX_API_READ_QUIET(priv, SYS_MISC_FW_VERSION, ver) ++ && ver.iv_major) ++ dev_info(ds->dev, "flash: new firmware %u.%u.%u\n", ++ ver.iv_major, ver.iv_minor, ++ le16_to_cpu(ver.iv_revision)); ++ } ++ ++ /* Silently discard all API commands during the teardown that ++ * reprobe triggers -- the switch firmware has been reset and ++ * has no knowledge of the old configuration. ++ */ ++ priv->skip_teardown = true; ++ ++ reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL); ++ if (!reprobe) ++ return ret; ++ ++ if (!try_module_get(THIS_MODULE)) { ++ kfree(reprobe); ++ return ret; ++ } ++ ++ reprobe->dev = get_device(ds->dev); ++ INIT_DELAYED_WORK(&reprobe->dwork, mxl862xx_reprobe_work_fn); ++ schedule_delayed_work(&reprobe->dwork, msecs_to_jiffies(500)); ++ ++ return ret; ++} +--- /dev/null ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-fw.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++ ++#ifndef __MXL862XX_FW_H ++#define __MXL862XX_FW_H ++ ++#include ++ ++int mxl862xx_devlink_info_get(struct dsa_switch *ds, ++ struct devlink_info_req *req, ++ struct netlink_ext_ack *extack); ++int mxl862xx_devlink_flash_update(struct dsa_switch *ds, ++ struct devlink_flash_update_params *params, ++ struct netlink_ext_ack *extack); ++ ++#endif /* __MXL862XX_FW_H */ +--- a/drivers/net/dsa/mxl862xx/mxl862xx-host.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c +@@ -14,6 +14,7 @@ + #include + #include + #include "mxl862xx.h" ++#include "mxl862xx-cmd.h" + #include "mxl862xx-host.h" + + #define CTRL_BUSY_MASK BIT(15) +@@ -334,6 +335,12 @@ int mxl862xx_api_wrap(struct mxl862xx_pr + int ret, cmd_ret; + u16 max, crc, i; + ++ if (priv->skip_teardown) ++ return 0; ++ ++ if (priv->block_host && cmd != SYS_MISC_FW_UPDATE) ++ return -EBUSY; ++ + dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data); + + mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED); +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -22,6 +22,7 @@ + #include "mxl862xx.h" + #include "mxl862xx-api.h" + #include "mxl862xx-cmd.h" ++#include "mxl862xx-fw.h" + #include "mxl862xx-host.h" + #include "mxl862xx-phylink.h" + +@@ -4245,6 +4246,9 @@ static const struct dsa_switch_ops mxl86 + .get_pause_stats = mxl862xx_get_pause_stats, + .get_stats64 = mxl862xx_get_stats64, + .self_test = mxl862xx_serdes_self_test, ++ .devlink_info_get = mxl862xx_devlink_info_get, ++ .devlink_flash_update = mxl862xx_devlink_flash_update, ++ + }; + + static int mxl862xx_probe(struct mdio_device *mdiodev) +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -388,6 +388,8 @@ struct mxl862xx_priv { + u16 lag_bridge_ports[MXL862XX_MAX_LAG_IDS + 1]; + u8 trunk_hash; + int mirror_dest; ++ bool block_host; ++ bool skip_teardown; + struct delayed_work stats_work; + }; + diff --git a/target/linux/generic/pending-6.12/760-22-net-dsa-mxl862xx-implement-port-MTU-configuration.patch b/target/linux/generic/pending-6.12/760-22-net-dsa-mxl862xx-implement-port-MTU-configuration.patch new file mode 100644 index 00000000000..1869a9f17c9 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-22-net-dsa-mxl862xx-implement-port-MTU-configuration.patch @@ -0,0 +1,110 @@ +From 2cb9aeb3a8d7ebac20331e0a533dcfbd73fa4237 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 23:42:18 +0000 +Subject: [PATCH 31/35] net: dsa: mxl862xx: implement port MTU configuration + +The firmware exposes a global max_packet_len register via +MXL862XX_COMMON_CFGSET. Since this is switch-wide rather than +per-port, cache each port's requested MTU and program the register +with the maximum across all ports. The firmware call is skipped when +the effective maximum does not change. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx.c | 50 +++++++++++++++++++++++++++++ + drivers/net/dsa/mxl862xx/mxl862xx.h | 4 +++ + 2 files changed, 54 insertions(+) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -3768,6 +3769,53 @@ static int mxl862xx_set_ageing_time(stru + return ret; + } + ++static int mxl862xx_port_change_mtu(struct dsa_switch *ds, int port, ++ int new_mtu) ++{ ++ struct mxl862xx_priv *priv = ds->priv; ++ struct mxl862xx_cfg param = {}; ++ int i, old_max = 0, new_max = 0; ++ int ret; ++ ++ for (i = 0; i < ds->num_ports; i++) { ++ if (priv->ports[i].mtu > old_max) ++ old_max = priv->ports[i].mtu; ++ } ++ ++ priv->ports[port].mtu = new_mtu; ++ ++ for (i = 0; i < ds->num_ports; i++) { ++ if (priv->ports[i].mtu > new_max) ++ new_max = priv->ports[i].mtu; ++ } ++ ++ if (new_max != old_max) { ++ ret = MXL862XX_API_READ(priv, MXL862XX_COMMON_CFGGET, ++ param); ++ if (ret) ++ return ret; ++ ++ param.max_packet_len = cpu_to_le16(new_max + ++ VLAN_ETH_HLEN + ++ ETH_FCS_LEN); ++ ret = MXL862XX_API_WRITE(priv, MXL862XX_COMMON_CFGSET, ++ param); ++ if (ret) { ++ dev_err(ds->dev, ++ "failed to set MTU to %d: %pe\n", ++ new_mtu, ERR_PTR(ret)); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int mxl862xx_port_max_mtu(struct dsa_switch *ds, int port) ++{ ++ return U16_MAX - VLAN_ETH_HLEN - ETH_FCS_LEN; ++} ++ + static void mxl862xx_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state) + { +@@ -4215,6 +4263,8 @@ static const struct dsa_switch_ops mxl86 + .port_disable = mxl862xx_port_disable, + .port_fast_age = mxl862xx_port_fast_age, + .set_ageing_time = mxl862xx_set_ageing_time, ++ .port_change_mtu = mxl862xx_port_change_mtu, ++ .port_max_mtu = mxl862xx_port_max_mtu, + .port_bridge_join = mxl862xx_port_bridge_join, + .port_bridge_leave = mxl862xx_port_bridge_leave, + .port_pre_bridge_flags = mxl862xx_port_pre_bridge_flags, +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -249,6 +249,8 @@ struct mxl862xx_port_stats { + * @lag_hash_bits: hash field bitmask (MXL862XX_TRUNK_HASH_*) requested + * when this port joined its LAG; used to recompute the + * global trunk_hash when a LAG is destroyed ++ * @mtu: per-port requested MTU; the global switch register ++ * is set to the maximum across all ports + */ + struct mxl862xx_port { + struct mxl862xx_priv *priv; +@@ -278,6 +280,8 @@ struct mxl862xx_port { + struct dsa_lag *lag; + bool lag_tx_enabled; + u8 lag_hash_bits; ++ /* MTU */ ++ int mtu; + /* Hardware stats accumulation */ + struct mxl862xx_port_stats stats; + spinlock_t stats_lock; diff --git a/target/linux/generic/pending-6.12/760-23-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch b/target/linux/generic/pending-6.12/760-23-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch new file mode 100644 index 00000000000..bf4d2f0ca7d --- /dev/null +++ b/target/linux/generic/pending-6.12/760-23-net-dsa-mxl862xx-support-BR_HAIRPIN_MODE-bridge-flag.patch @@ -0,0 +1,106 @@ +From d55ca68eb0d20a66c32d531b0a454871b486c1b1 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 25 Mar 2026 01:47:19 +0000 +Subject: [PATCH 32/35] net: dsa: mxl862xx: support BR_HAIRPIN_MODE bridge flag + +Implement hairpin mode by including the port's own bridge port ID in +its forwarding portmap. When hairpin is enabled, bridged frames whose +destination resolves to the ingress port are allowed to egress there +instead of being dropped. + +For LAG ports, the LAG's dedicated bridge port is added to the +master's portmap, which naturally propagates to the LAG bridge port +via the second loop in sync_bridge_members. + +The port_bridge_flags handler toggles the bit directly on the cached +portmap and pushes the update via set_bridge_port, avoiding a full +bridge member rebuild since only the calling port is affected. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx.c | 30 ++++++++++++++++++++++++++++- + drivers/net/dsa/mxl862xx/mxl862xx.h | 6 ++++++ + 2 files changed, 35 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -811,6 +811,15 @@ static int mxl862xx_sync_bridge_members( + __set_bit(mxl862xx_cpu_bridge_port_id(ds, port), + priv->ports[port].portmap); + ++ /* Hairpin: include the port's own bridge port so bridged ++ * frames can egress the ingress port. ++ * For LAG ports this adds the LAG bridge port, which ++ * propagates to the LAG BP in the second loop below. ++ */ ++ if (priv->ports[port].hairpin) ++ __set_bit(mxl862xx_lag_bridge_port(priv, port), ++ priv->ports[port].portmap); ++ + err = mxl862xx_set_bridge_port(ds, port); + if (err) + ret = err; +@@ -3939,7 +3948,7 @@ static int mxl862xx_port_pre_bridge_flag + struct netlink_ext_ack *extack) + { + if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | +- BR_LEARNING)) ++ BR_LEARNING | BR_HAIRPIN_MODE)) + return -EINVAL; + + return 0; +@@ -3954,6 +3963,7 @@ static int mxl862xx_port_bridge_flags(st + unsigned long block = old_block; + bool need_update = false; + int ret; ++ u16 bp; + + if (flags.mask & BR_FLOOD) { + if (flags.val & BR_FLOOD) +@@ -3988,6 +3998,24 @@ static int mxl862xx_port_bridge_flags(st + ret = mxl862xx_set_bridge_port(ds, port); + if (ret) + return ret; ++ } ++ ++ if (flags.mask & BR_HAIRPIN_MODE) { ++ bp = mxl862xx_lag_bridge_port(priv, port); ++ priv->ports[port].hairpin = !!(flags.val & BR_HAIRPIN_MODE); ++ ++ /* Hairpin adds/removes the port's own bridge port from its ++ * cached portmap. Only this port is affected -- push the ++ * updated portmap directly. ++ */ ++ if (flags.val & BR_HAIRPIN_MODE) ++ __set_bit(bp, priv->ports[port].portmap); ++ else ++ __clear_bit(bp, priv->ports[port].portmap); ++ ++ ret = mxl862xx_set_bridge_port(ds, port); ++ if (ret) ++ return ret; + } + + return 0; +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -241,6 +241,10 @@ struct mxl862xx_port_stats { + * @stats_lock: protects accumulator reads in .get_stats64 against + * concurrent updates from the polling work + * @tag_8021q_vid: currently assigned tag_8021q management VID ++ * @hairpin: true when hairpin mode is active (BR_HAIRPIN_MODE); ++ * the port's own bridge port is included in its ++ * portmap so bridged frames can egress the ingress ++ * port + * @ingress_mirror: true when ingress mirroring is active on this port + * @egress_mirror: true when egress mirroring is active on this port + * @lag: non-NULL when port is member of a LAG group; +@@ -273,6 +277,8 @@ struct mxl862xx_port { + struct work_struct host_flood_work; + u16 tag_8021q_vid; + struct mxl862xx_evlan_block cpu_egress_evlan; ++ /* Hairpin state */ ++ bool hairpin; + /* Mirror state */ + bool ingress_mirror; + bool egress_mirror; diff --git a/target/linux/generic/pending-6.12/760-24-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch b/target/linux/generic/pending-6.12/760-24-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch new file mode 100644 index 00000000000..a524d157e51 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-24-net-dsa-mxl862xx-support-BR_ISOLATED-bridge-flag.patch @@ -0,0 +1,95 @@ +From 74b6654ba74eb142340de4c51b97c0221cfcae37 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 25 Mar 2026 01:51:33 +0000 +Subject: [PATCH 33/35] net: dsa: mxl862xx: support BR_ISOLATED bridge flag + +Implement port isolation by excluding isolated ports from each other's +forwarding portmaps in sync_bridge_members. Non-isolated ports can +still reach isolated ports and vice versa -- only isolated-to-isolated +forwarding is blocked. + +When the isolation state changes, all bridge members' portmaps are +rebuilt via sync_bridge_members since multiple ports are affected. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx.c | 26 +++++++++++++++++++++++++- + drivers/net/dsa/mxl862xx/mxl862xx.h | 4 ++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -802,6 +802,14 @@ static int mxl862xx_sync_bridge_members( + */ + if (!mxl862xx_is_lag_master(priv, member)) + continue; ++ ++ /* Isolated ports cannot forward to each other. ++ * Non-isolated ports can reach everyone. ++ */ ++ if (priv->ports[port].isolated && ++ priv->ports[member].isolated) ++ continue; ++ + if (member != port) { + bp = mxl862xx_lag_bridge_port(priv, + member); +@@ -3948,7 +3956,7 @@ static int mxl862xx_port_pre_bridge_flag + struct netlink_ext_ack *extack) + { + if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | +- BR_LEARNING | BR_HAIRPIN_MODE)) ++ BR_LEARNING | BR_HAIRPIN_MODE | BR_ISOLATED)) + return -EINVAL; + + return 0; +@@ -3962,6 +3970,7 @@ static int mxl862xx_port_bridge_flags(st + unsigned long old_block = priv->ports[port].flood_block; + unsigned long block = old_block; + bool need_update = false; ++ struct dsa_port *dp; + int ret; + u16 bp; + +@@ -4018,6 +4027,21 @@ static int mxl862xx_port_bridge_flags(st + return ret; + } + ++ if (flags.mask & BR_ISOLATED) { ++ dp = dsa_to_port(ds, port); ++ priv->ports[port].isolated = !!(flags.val & BR_ISOLATED); ++ ++ /* Isolation affects all bridge members' portmaps: ++ * isolated ports must be removed from each other's ++ * portmaps. Rebuild all portmaps for this bridge. ++ */ ++ if (dp->bridge) { ++ ret = mxl862xx_sync_bridge_members(ds, dp->bridge); ++ if (ret) ++ return ret; ++ } ++ } ++ + return 0; + } + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.h +@@ -214,6 +214,9 @@ struct mxl862xx_port_stats { + * @flood_block: bitmask of firmware meter indices that are currently + * rate-limiting flood traffic on this port (zero-rate + * meters used to block flooding) ++ * @isolated: true when port isolation is active (BR_ISOLATED); ++ * isolated ports are excluded from each other's ++ * forwarding portmaps + * @learning: true when address learning is enabled on this port + * @setup_done: set at end of port_setup, cleared at start of + * port_teardown; guards deferred work against +@@ -261,6 +264,7 @@ struct mxl862xx_port { + u16 fid; + DECLARE_BITMAP(portmap, MXL862XX_MAX_BRIDGE_PORTS); + unsigned long flood_block; ++ bool isolated; + bool learning; + bool setup_done; + /* VLAN state */ diff --git a/target/linux/generic/pending-6.12/760-25-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch b/target/linux/generic/pending-6.12/760-25-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch new file mode 100644 index 00000000000..98cfb44d489 --- /dev/null +++ b/target/linux/generic/pending-6.12/760-25-DO-NOT-SUBMIT-net-dsa-mxl862xx-re-introduce-PCE-work.patch @@ -0,0 +1,81 @@ +From 0902a6790750714445c75a66d60f1bc4897126ce Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 18:17:49 +0000 +Subject: [PATCH 34/35] DO NOT SUBMIT: net: dsa: mxl862xx: re-introduce PCE + workaround for old firmware + +Re-introduce the mxl862xx_disable_fw_global_rules() function that +disables firmware default global PCE rules for firmware versions +older than 1.0.80. The upstream submission replaced this with a +dev_warn() since firmware >= 1.0.80 no longer installs these rules, +but downstream deployments may still run older firmware. + +This commit is for downstream use only and must not be submitted +upstream. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx.c | 45 +++++++++++++++++++++++++++-- + 1 file changed, 42 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx.c +@@ -477,6 +477,43 @@ static int mxl862xx_setup_drop_meter(str + return MXL862XX_API_WRITE(priv, MXL862XX_COMMON_REGISTERMOD, reg); + } + ++/* Disable firmware global PCE rules that trap various protocols to the ++ * on-die microcontroller (port 0) via PORTMAP_CPU. Under DSA, these ++ * frames must either reach the host CPU via per-port rules (link-local) ++ * or through the normal bridge forwarding path (ARP broadcast), so the ++ * global firmware rules are not needed. With the microcontroller port ++ * disabled they would silently drop matching traffic. ++ * ++ * Global rules have lower indices than CTP rules, hence higher priority ++ * in the PCE pipeline -- they must be explicitly disabled or they will ++ * shadow the per-CTP traps. ++ * ++ * Indices from gsw_flow_index.h: ++ * 1 -- BPDU (STP/RSTP, dst 01:80:c2:00:00:00) ++ * 3 -- LLDP (EtherType 0x88cc) ++ * 4 -- OAM/LACP (EtherType 0x8809) ++ * 6 -- System MAC (dst 02:e0:92:00:00:01, vendor management MAC) ++ * 7 -- ARP Request (broadcast + EtherType 0x0806 + TPA 192.0.2.1) ++ */ ++static int mxl862xx_disable_fw_global_rules(struct dsa_switch *ds) ++{ ++ static const u16 indices[] = { 1, 3, 4, 6, 7 }; ++ struct mxl862xx_pce_rule rule; ++ int i, ret; ++ ++ for (i = 0; i < ARRAY_SIZE(indices); i++) { ++ memset(&rule, 0, sizeof(rule)); ++ rule.pattern.index = cpu_to_le16(indices[i]); ++ /* pattern.enable == 0 -> rule is disabled */ ++ ++ ret = MXL862XX_API_WRITE(ds->priv, ++ MXL862XX_TFLOW_PCERULEWRITE, rule); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} + + /* Per-CTP offset used for the link-local trap rule. Each port's CTP + * flow-table block is pre-allocated by the firmware during init (44 +@@ -1154,9 +1191,11 @@ static int mxl862xx_setup(struct dsa_swi + if (ret) + return ret; + +- if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80)) +- dev_warn(ds->dev, "firmware < 1.0.80 installs global PCE rules " +- "that interfere with DSA operation, please update\n"); ++ if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80)) { ++ ret = mxl862xx_disable_fw_global_rules(ds); ++ if (ret) ++ return ret; ++ } + + /* Pre-allocate firmware resources for all ports. The DSA core + * calls change_tag_protocol() between setup() and port_setup(), diff --git a/target/linux/generic/pending-6.12/760-26-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch b/target/linux/generic/pending-6.12/760-26-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch new file mode 100644 index 00000000000..41cd927ac4f --- /dev/null +++ b/target/linux/generic/pending-6.12/760-26-DO-NOT-SUBMIT-net-dsa-mxl862xx-legacy-SFP-API-fallba.patch @@ -0,0 +1,251 @@ +From 0ac876d5b952218ab79ea0a0815cf6fd1290b1d0 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 24 Mar 2026 18:19:56 +0000 +Subject: [PATCH 35/35] DO NOT SUBMIT: net: dsa: mxl862xx: legacy SFP API + fallback for old firmware + +Re-introduce the SYS_MISC_SFP_SET-based PCS implementation as a +fallback for firmware versions older than 1.0.80 which lack the +XPCS API. mxl862xx_setup_pcs() selects between the XPCS ops and +legacy SFP ops based on firmware version. + +This commit is for downstream use only and must not be submitted +upstream. + +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mxl862xx/mxl862xx-api.h | 22 +++ + drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 1 + + drivers/net/dsa/mxl862xx/mxl862xx-phylink.c | 160 +++++++++++++++++++- + 3 files changed, 178 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h +@@ -2400,6 +2400,28 @@ struct mxl862xx_sys_fw_image_version { + } __packed; + + /** ++ * struct mxl862xx_sys_sfp_cfg - legacy SFP/SerDes port configuration ++ * @port_id: port id (0 or 1) ++ * @option: config options (0 - SFP mode/speed/link-status, 1 - flow control) ++ * @mode: SFP mode (0 - auto, 1 - fix, 2 - disable) ++ * @speed: select speed when mode is 1 ++ * @link: get link state ++ * @fc_en: flow control (0 - disable, 1 - enable) ++ */ ++struct mxl862xx_sys_sfp_cfg { ++ u8 port_id:4; ++ u8 option:4; ++ union { ++ struct { ++ u8 mode; ++ u8 speed; ++ u8 link; ++ }; ++ u8 fc_en; ++ }; ++} __packed; ++ ++/** + * enum mxl862xx_rmon_port_type - RMON counter table type + * @MXL862XX_RMON_CTP_PORT_RX: CTP RX counters + * @MXL862XX_RMON_CTP_PORT_TX: CTP TX counters +--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +@@ -85,6 +85,7 @@ + + #define SYS_MISC_FW_UPDATE (SYS_MISC_MAGIC + 0x1) + #define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2) ++#define SYS_MISC_SFP_SET (SYS_MISC_MAGIC + 0xe) + + #define MXL862XX_XPCS_MAGIC 0x1a00 + #define MXL862XX_XPCS_PCS_CONFIG (MXL862XX_XPCS_MAGIC + 0x1) +--- a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c ++++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c +@@ -52,6 +52,155 @@ static struct mxl862xx_pcs *pcs_to_mxl86 + return container_of(pcs, struct mxl862xx_pcs, pcs); + } + ++/* Legacy SFP-based PCS implementation for firmware < 1.0.80 */ ++static int mxl862xx_legacy_pcs_config(struct phylink_pcs *pcs, ++ unsigned int neg_mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mxl862xx_priv *priv = pcs_to_mxl862xx_pcs(pcs)->priv; ++ int port = pcs_to_mxl862xx_pcs(pcs)->port; ++ struct mxl862xx_sys_sfp_cfg ser_intf = { ++ .option = 0, ++ .mode = 1, ++ }; ++ ++ if (port != 9 && port != 13) ++ return 0; ++ ++ if (port == 9) ++ ser_intf.port_id = 0; ++ else ++ ser_intf.port_id = 1; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ ser_intf.speed = 8; ++ break; ++ case PHY_INTERFACE_MODE_1000BASEX: ++ ser_intf.speed = (neg_mode & PHYLINK_PCS_NEG_INBAND) ? 1 : 7; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ ser_intf.speed = 4; ++ break; ++ case PHY_INTERFACE_MODE_10GBASER: ++ ser_intf.speed = 2; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ ser_intf.speed = 3; ++ break; ++ default: ++ dev_err(priv->ds->dev, "unsupported interface: %s\n", ++ phy_modes(interface)); ++ return -EINVAL; ++ } ++ ++ return MXL862XX_API_WRITE(priv, SYS_MISC_SFP_SET, ser_intf); ++} ++ ++static void mxl862xx_legacy_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mxl862xx_priv *priv = pcs_to_mxl862xx_pcs(pcs)->priv; ++ int port = pcs_to_mxl862xx_pcs(pcs)->port; ++ struct mxl862xx_port_link_cfg port_link_cfg = { ++ .port_id = port, ++ }; ++ struct mxl862xx_port_cfg port_cfg = { ++ .port_id = port, ++ }; ++ int ret; ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_COMMON_PORTLINKCFGGET, ++ port_link_cfg); ++ if (ret) ++ return; ++ ++ ret = MXL862XX_API_READ(priv, MXL862XX_COMMON_PORTCFGGET, port_cfg); ++ if (ret) ++ return; ++ ++ state->link = (port_link_cfg.link == MXL862XX_PORT_LINK_UP); ++ state->an_complete = state->link; ++ ++ switch (port_link_cfg.speed) { ++ case MXL862XX_PORT_SPEED_10: ++ state->speed = SPEED_10; ++ break; ++ case MXL862XX_PORT_SPEED_100: ++ state->speed = SPEED_100; ++ break; ++ case MXL862XX_PORT_SPEED_1000: ++ state->speed = SPEED_1000; ++ break; ++ case MXL862XX_PORT_SPEED_2500: ++ state->speed = SPEED_2500; ++ break; ++ case MXL862XX_PORT_SPEED_5000: ++ state->speed = SPEED_5000; ++ break; ++ case MXL862XX_PORT_SPEED_10000: ++ state->speed = SPEED_10000; ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ break; ++ } ++ ++ switch (port_link_cfg.duplex) { ++ case MXL862XX_DUPLEX_HALF: ++ state->duplex = DUPLEX_HALF; ++ break; ++ case MXL862XX_DUPLEX_FULL: ++ state->duplex = DUPLEX_FULL; ++ break; ++ default: ++ state->duplex = DUPLEX_UNKNOWN; ++ break; ++ } ++ ++ state->pause &= ~(MLO_PAUSE_RX | MLO_PAUSE_TX); ++ switch (port_cfg.flow_ctrl) { ++ case MXL862XX_FLOW_RXTX: ++ state->pause |= MLO_PAUSE_TXRX_MASK; ++ break; ++ case MXL862XX_FLOW_TX: ++ state->pause |= MLO_PAUSE_TX; ++ break; ++ case MXL862XX_FLOW_RX: ++ state->pause |= MLO_PAUSE_RX; ++ break; ++ case MXL862XX_FLOW_OFF: ++ default: ++ break; ++ } ++} ++ ++static unsigned int ++mxl862xx_legacy_pcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_USXGMII: ++ return LINK_INBAND_ENABLE; ++ case PHY_INTERFACE_MODE_1000BASEX: ++ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return LINK_INBAND_DISABLE; ++ default: ++ return 0; ++ } ++} ++ ++static const struct phylink_pcs_ops mxl862xx_legacy_pcs_ops = { ++ .pcs_get_state = mxl862xx_legacy_pcs_get_state, ++ .pcs_config = mxl862xx_legacy_pcs_config, ++ .pcs_inband_caps = mxl862xx_legacy_pcs_inband_caps, ++}; ++ + static int mxl862xx_xpcs_port_id(int port) + { + return port >= 13; +@@ -389,7 +538,10 @@ void mxl862xx_setup_pcs(struct mxl862xx_ + pcs->priv = priv; + pcs->port = port; + +- pcs->pcs.ops = &mxl862xx_pcs_ops; ++ if (MXL862XX_FW_VER_MIN(priv, 1, 0, 80)) ++ pcs->pcs.ops = &mxl862xx_pcs_ops; ++ else ++ pcs->pcs.ops = &mxl862xx_legacy_pcs_ops; + pcs->pcs.poll = true; + } + +@@ -401,9 +553,6 @@ mxl862xx_phylink_mac_select_pcs(struct p + struct mxl862xx_priv *priv = dp->ds->priv; + int port = dp->index; + +- if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 80)) +- return NULL; +- + switch (port) { + case 9 ... 16: + return &priv->serdes_ports[port - 9].pcs; +@@ -534,7 +683,7 @@ void mxl862xx_serdes_get_stats(struct ds + } + + void mxl862xx_serdes_self_test(struct dsa_switch *ds, int port, +- struct ethtool_test *etest, u64 *data) ++ struct ethtool_test *etest, u64 *data) + { + struct mxl862xx_xpcs_prbs_cfg prbs = {}; + struct mxl862xx_xpcs_bert_cfg bert = {}; diff --git a/target/linux/generic/pending-6.12/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch b/target/linux/generic/pending-6.12/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch new file mode 100644 index 00000000000..39ba71606ec --- /dev/null +++ b/target/linux/generic/pending-6.12/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch @@ -0,0 +1,61 @@ +From patchwork Thu Aug 5 22:23:30 2021 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12422209 +Date: Thu, 5 Aug 2021 23:23:30 +0100 +From: Daniel Golle +To: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org +Cc: "David S. Miller" , Andrew Lunn , + Michael Walle +Subject: [PATCH] ARM: kirkwood: add missing for ETH_ALEN +Message-ID: +MIME-Version: 1.0 +Content-Disposition: inline +X-BeenThere: linux-arm-kernel@lists.infradead.org +X-Mailman-Version: 2.1.34 +Precedence: list +List-Id: +List-Archive: +Sender: "linux-arm-kernel" + +After commit 83216e3988cd1 ("of: net: pass the dst buffer to +of_get_mac_address()") build fails for kirkwood as ETH_ALEN is not +defined. + +arch/arm/mach-mvebu/kirkwood.c: In function 'kirkwood_dt_eth_fixup': +arch/arm/mach-mvebu/kirkwood.c:87:13: error: 'ETH_ALEN' undeclared (first use in this function); did you mean 'ESTALE'? + u8 tmpmac[ETH_ALEN]; + ^~~~~~~~ + ESTALE +arch/arm/mach-mvebu/kirkwood.c:87:13: note: each undeclared identifier is reported only once for each function it appears in +arch/arm/mach-mvebu/kirkwood.c:87:6: warning: unused variable 'tmpmac' [-Wunused-variable] + u8 tmpmac[ETH_ALEN]; + ^~~~~~ +make[5]: *** [scripts/Makefile.build:262: arch/arm/mach-mvebu/kirkwood.o] Error 1 +make[5]: *** Waiting for unfinished jobs.... + +Add missing #include to fix this. + +Cc: David S. Miller +Cc: Andrew Lunn +Cc: Michael Walle +Reported-by: https://buildbot.openwrt.org/master/images/#/builders/56/builds/220/steps/44/logs/stdio +Fixes: 83216e3988cd1 ("of: net: pass the dst buffer to of_get_mac_address()") +Signed-off-by: Daniel Golle +--- + arch/arm/mach-mvebu/kirkwood.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/mach-mvebu/kirkwood.c ++++ b/arch/arm/mach-mvebu/kirkwood.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include diff --git a/target/linux/generic/pending-6.12/790-bus-mhi-core-add-SBL-state-callback.patch b/target/linux/generic/pending-6.12/790-bus-mhi-core-add-SBL-state-callback.patch new file mode 100644 index 00000000000..80d13061158 --- /dev/null +++ b/target/linux/generic/pending-6.12/790-bus-mhi-core-add-SBL-state-callback.patch @@ -0,0 +1,48 @@ +From 5f7c5e1c0d7a79be144e5efc1f24728ddd7fc25c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 5 Nov 2022 20:02:56 +0100 +Subject: [PATCH 1/2] bus: mhi: core: add SBL state callback + +Add support for SBL state callback in MHI core. + +It is required for ath11k MHI devices in order to be able to set QRTR +instance ID in the SBL state so that QRTR instance ID-s dont conflict in +case of multiple PCI/MHI cards or AHB + PCI/MHI card. +Setting QRTR instance ID is only possible in SBL state and there is +currently no way to ensure that we are in that state, so provide a +callback that the controller can trigger off. + +Signed-off-by: Robert Marko +--- + drivers/bus/mhi/host/main.c | 1 + + include/linux/mhi.h | 2 ++ + 2 files changed, 3 insertions(+) + +--- a/drivers/bus/mhi/host/main.c ++++ b/drivers/bus/mhi/host/main.c +@@ -916,6 +916,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_ + switch (event) { + case MHI_EE_SBL: + st = DEV_ST_TRANSITION_SBL; ++ mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_SBL_MODE); + break; + case MHI_EE_WFW: + case MHI_EE_AMSS: +--- a/include/linux/mhi.h ++++ b/include/linux/mhi.h +@@ -34,6 +34,7 @@ struct mhi_buf_info; + * @MHI_CB_SYS_ERROR: MHI device entered error state (may recover) + * @MHI_CB_FATAL_ERROR: MHI device entered fatal error state + * @MHI_CB_BW_REQ: Received a bandwidth switch request from device ++ * @MHI_CB_EE_SBL_MODE: MHI device entered SBL mode + */ + enum mhi_callback { + MHI_CB_IDLE, +@@ -45,6 +46,7 @@ enum mhi_callback { + MHI_CB_SYS_ERROR, + MHI_CB_FATAL_ERROR, + MHI_CB_BW_REQ, ++ MHI_CB_EE_SBL_MODE, + }; + + /** diff --git a/target/linux/generic/pending-6.12/791-tg3-Fix-DMA-allocations-on-57766-devices.patch b/target/linux/generic/pending-6.12/791-tg3-Fix-DMA-allocations-on-57766-devices.patch new file mode 100644 index 00000000000..5181041ec01 --- /dev/null +++ b/target/linux/generic/pending-6.12/791-tg3-Fix-DMA-allocations-on-57766-devices.patch @@ -0,0 +1,31 @@ +From f992b15965177e2f280fb6f41f292214f9a6f8d5 Mon Sep 17 00:00:00 2001 +From: Pavan Chebbi +Date: Tue, 10 Dec 2024 03:28:31 -0800 +Subject: [PATCH] tg3: Fix DMA allocations on 57766 devices + +The coherent DMA mask of 31b may not be accepted if +the DMA mask is configured to use higher memories of +64b. Set the DMA mask also to lower 32b for 57766 +devices. + +Fixes: 614f4d166eee ("tg3: Set coherent DMA mask bits to 31 for BCM57766 chipsets") +Reported-By: Rui Salvaterra +Signed-off-by: Pavan Chebbi +--- + drivers/net/ethernet/broadcom/tg3.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/broadcom/tg3.c ++++ b/drivers/net/ethernet/broadcom/tg3.c +@@ -17799,8 +17799,10 @@ static int tg3_init_one(struct pci_dev * + } else + persist_dma_mask = dma_mask = DMA_BIT_MASK(64); + +- if (tg3_asic_rev(tp) == ASIC_REV_57766) ++ if (tg3_asic_rev(tp) == ASIC_REV_57766) { ++ dma_mask = DMA_BIT_MASK(32); + persist_dma_mask = DMA_BIT_MASK(31); ++ } + + /* Configure DMA attributes. */ + if (dma_mask > DMA_BIT_MASK(32)) { diff --git a/target/linux/generic/pending-6.12/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch b/target/linux/generic/pending-6.12/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch new file mode 100644 index 00000000000..4a9c188d179 --- /dev/null +++ b/target/linux/generic/pending-6.12/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch @@ -0,0 +1,73 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Subject: [PATCH] bcma: get SoC device struct & copy its DMA params to the + subdevices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For bus devices to be fully usable it's required to set their DMA +parameters. + +For years it has been missing and remained unnoticed because of +mips_dma_alloc_coherent() silently handling the empty coherent_dma_mask. +Kernel 4.19 came with a lot of DMA changes and caused a regression on +the bcm47xx. Starting with the commit f8c55dc6e828 ("MIPS: use generic +dma noncoherent ops for simple noncoherent platforms") DMA coherent +allocations just fail. Example: +[ 1.114914] bgmac_bcma bcma0:2: Allocation of TX ring 0x200 failed +[ 1.121215] bgmac_bcma bcma0:2: Unable to alloc memory for DMA +[ 1.127626] bgmac_bcma: probe of bcma0:2 failed with error -12 +[ 1.133838] bgmac_bcma: Broadcom 47xx GBit MAC driver loaded + +This change fixes above regression in addition to the MIPS bcm47xx +commit 321c46b91550 ("MIPS: BCM47XX: Setup struct device for the SoC"). + +It also fixes another *old* GPIO regression caused by a parent pointing +to the NULL: +[ 0.157054] missing gpiochip .dev parent pointer +[ 0.157287] bcma: bus0: Error registering GPIO driver: -22 +introduced by the commit 74f4e0cc6108 ("bcma: switch GPIO portions to +use GPIOLIB_IRQCHIP"). + +Fixes: f8c55dc6e828 ("MIPS: use generic dma noncoherent ops for simple noncoherent platforms") +Fixes: 74f4e0cc6108 ("bcma: switch GPIO portions to use GPIOLIB_IRQCHIP") +Cc: linux-mips@linux-mips.org +Cc: Christoph Hellwig +Cc: Linus Walleij +Signed-off-by: Rafał Miłecki +--- + +--- a/drivers/bcma/host_soc.c ++++ b/drivers/bcma/host_soc.c +@@ -191,6 +191,8 @@ int __init bcma_host_soc_init(struct bcm + struct bcma_bus *bus = &soc->bus; + int err; + ++ bus->dev = soc->dev; ++ + /* Scan bus and initialize it */ + err = bcma_bus_early_register(bus); + if (err) +--- a/drivers/bcma/main.c ++++ b/drivers/bcma/main.c +@@ -237,13 +237,17 @@ EXPORT_SYMBOL(bcma_core_irq); + + void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core) + { +- device_initialize(&core->dev); ++ struct device *dev = &core->dev; ++ ++ device_initialize(dev); + core->dev.release = bcma_release_core_dev; + core->dev.bus = &bcma_bus_type; +- dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index); ++ dev_set_name(dev, "bcma%d:%d", bus->num, core->core_index); + core->dev.parent = bus->dev; +- if (bus->dev) ++ if (bus->dev) { + bcma_of_fill_device(bus->dev, core); ++ dma_coerce_mask_and_coherent(dev, bus->dev->coherent_dma_mask); ++ } + + switch (bus->hosttype) { + case BCMA_HOSTTYPE_PCI: 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 new file mode 100644 index 00000000000..042afdc391b --- /dev/null +++ b/target/linux/generic/pending-6.12/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch @@ -0,0 +1,134 @@ +From fd59b838dd90452f61a17dc9e5ff175205003068 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 15 Sep 2022 18:49:43 +0200 +Subject: [PATCH] OPP: Provide old opp to config_clks on _set_opp + +With the target opp, also pass the old opp to config_clks function. +This can be useful when a driver needs to take decision on what fequency +to set based on what is the current frequency without using a +clk_get_freq call. +Update the only user of custom config_clks (tegra30 devfreq driver) to +this new implementation. + +Signed-off-by: Christian Marangi +--- + drivers/devfreq/tegra30-devfreq.c | 5 +++-- + drivers/opp/core.c | 11 ++++++----- + include/linux/pm_opp.h | 11 ++++++----- + 3 files changed, 15 insertions(+), 12 deletions(-) + +--- a/drivers/devfreq/tegra30-devfreq.c ++++ b/drivers/devfreq/tegra30-devfreq.c +@@ -823,8 +823,9 @@ static int devm_tegra_devfreq_init_hw(st + + static int tegra_devfreq_config_clks_nop(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) + { + /* We want to skip clk configuration via dev_pm_opp_set_opp() */ + return 0; +--- a/drivers/opp/core.c ++++ b/drivers/opp/core.c +@@ -965,7 +965,8 @@ static int _set_opp_voltage(struct devic + + static int + _opp_config_clk_single(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) + { + unsigned long *target = data; + unsigned long freq; +@@ -997,8 +998,8 @@ _opp_config_clk_single(struct device *de + * the order in which they are present in the array while scaling up. + */ + int dev_pm_opp_config_clks_simple(struct device *dev, +- struct opp_table *opp_table, struct dev_pm_opp *opp, void *data, +- bool scaling_down) ++ struct opp_table *opp_table, struct dev_pm_opp *old_opp, ++ struct dev_pm_opp *opp, void *data, bool scaling_down) + { + int ret, i; + +@@ -1265,7 +1266,7 @@ static int _set_opp(struct device *dev, + } + + if (opp_table->config_clks) { +- ret = opp_table->config_clks(dev, opp_table, opp, clk_data, scaling_down); ++ ret = opp_table->config_clks(dev, opp_table, old_opp, opp, clk_data, scaling_down); + if (ret) + return ret; + } +@@ -1344,7 +1345,7 @@ int dev_pm_opp_set_rate(struct device *d + * equivalent to a clk_set_rate() + */ + if (!_get_opp_count(opp_table)) { +- ret = opp_table->config_clks(dev, opp_table, NULL, ++ ret = opp_table->config_clks(dev, opp_table, NULL, NULL, + &target_freq, false); + goto put_opp_table; + } +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -1122,8 +1122,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 + struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp, + struct regulator **regulators, unsigned int count); + +-typedef int (*config_clks_t)(struct device *dev, struct opp_table *opp_table, ++typedef int (*config_clks_t)(struct device *dev, ++ struct opp_table *opp_table, struct dev_pm_opp *old_opp, + struct dev_pm_opp *opp, void *data, bool scaling_down); + + /** +@@ -184,8 +185,8 @@ int dev_pm_opp_set_config(struct device + int devm_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config); + void dev_pm_opp_clear_config(int token); + int dev_pm_opp_config_clks_simple(struct device *dev, +- struct opp_table *opp_table, struct dev_pm_opp *opp, void *data, +- bool scaling_down); ++ struct opp_table *opp_table, struct dev_pm_opp *old_opp, ++ struct dev_pm_opp *opp, void *data, bool scaling_down); + + struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, struct opp_table *dst_table, struct dev_pm_opp *src_opp); + int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); +@@ -395,8 +396,8 @@ static inline int devm_pm_opp_set_config + static inline void dev_pm_opp_clear_config(int token) {} + + static inline int dev_pm_opp_config_clks_simple(struct device *dev, +- struct opp_table *opp_table, struct dev_pm_opp *opp, void *data, +- bool scaling_down) ++ struct opp_table *opp_table, struct dev_pm_opp *old_opp, ++ struct dev_pm_opp *opp, void *data, bool scaling_down) + { + return -EOPNOTSUPP; + } +--- a/include/ufs/ufshcd.h ++++ b/include/ufs/ufshcd.h +@@ -1333,8 +1333,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/generic/pending-6.12/804-nvmem-core-support-mac-base-fixed-layout-cells.patch b/target/linux/generic/pending-6.12/804-nvmem-core-support-mac-base-fixed-layout-cells.patch new file mode 100644 index 00000000000..8f2706e6b42 --- /dev/null +++ b/target/linux/generic/pending-6.12/804-nvmem-core-support-mac-base-fixed-layout-cells.patch @@ -0,0 +1,124 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 13 Jul 2023 18:29:19 +0200 +Subject: [PATCH] nvmem: core: support "mac-base" fixed layout cells + +Fixed layout binding allows specifying "mac-base" NVMEM cells. It's used +for base MAC address (that can be used for calculating relative +addresses). It can be stored in a raw binary format or as an ASCII +string. +--- + +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -2,6 +2,7 @@ + menuconfig NVMEM + bool "NVMEM Support" + imply NVMEM_LAYOUTS ++ select GENERIC_NET_UTILS + help + Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -7,9 +7,12 @@ + */ + + #include ++#include ++#include + #include + #include + #include ++#include + #include + #include + #include +@@ -811,6 +814,62 @@ static int nvmem_validate_keepouts(struc + return 0; + } + ++static int nvmem_mac_base_raw_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ if (WARN_ON(bytes != ETH_ALEN)) ++ return -EINVAL; ++ ++ if (index) ++ eth_addr_add(buf, index); ++ ++ return 0; ++} ++ ++static int nvmem_mac_base_ascii_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ u8 mac[ETH_ALEN]; ++ ++ if (WARN_ON(bytes != 3 * ETH_ALEN - 1)) ++ return -EINVAL; ++ ++ if (!mac_pton(buf, mac)) ++ return -EINVAL; ++ ++ if (index) ++ eth_addr_add(mac, index); ++ ++ ether_addr_copy(buf, mac); ++ ++ return 0; ++} ++ ++static int nvmem_mac_base_hex_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ u8 mac[ETH_ALEN], *hexstr; ++ int i; ++ ++ if (WARN_ON(bytes != 2 * ETH_ALEN)) ++ return -EINVAL; ++ ++ hexstr = (u8 *)buf; ++ for (i = 0; i < ETH_ALEN; i++) { ++ if (!isxdigit(hexstr[i * 2]) || !isxdigit(hexstr[i * 2 + 1])) ++ return -EINVAL; ++ ++ mac[i] = (hex_to_bin(hexstr[i * 2]) << 4) | hex_to_bin(hexstr[i * 2 + 1]); ++ } ++ ++ if (index) ++ eth_addr_add(mac, index); ++ ++ ether_addr_copy(buf, mac); ++ ++ return 0; ++} ++ + static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) + { + struct device *dev = &nvmem->dev; +@@ -852,6 +911,25 @@ static int nvmem_add_cells_from_dt(struc + if (nvmem->fixup_dt_cell_info) + nvmem->fixup_dt_cell_info(nvmem, &info); + ++ if (of_device_is_compatible(np, "fixed-layout")) { ++ if (of_device_is_compatible(child, "mac-base")) { ++ if (info.bytes == ETH_ALEN) { ++ info.raw_len = info.bytes; ++ info.bytes = ETH_ALEN; ++ info.read_post_process = nvmem_mac_base_raw_read; ++ } else if (info.bytes == 2 * ETH_ALEN) { ++ info.raw_len = info.bytes; ++ info.bytes = ETH_ALEN; ++ info.read_post_process = nvmem_mac_base_hex_read; ++ } else if (info.bytes == 3 * ETH_ALEN - 1) { ++ info.raw_len = info.bytes; ++ info.bytes = ETH_ALEN; ++ info.read_post_process = nvmem_mac_base_ascii_read; ++ } ++ ++ } ++ } ++ + ret = nvmem_add_one_cell(nvmem, &info); + kfree(info.name); + if (ret) { diff --git a/target/linux/generic/pending-6.12/809-01-nvmem-core-generalize-mac-base-cells-handling.patch b/target/linux/generic/pending-6.12/809-01-nvmem-core-generalize-mac-base-cells-handling.patch new file mode 100644 index 00000000000..94d43e0ce09 --- /dev/null +++ b/target/linux/generic/pending-6.12/809-01-nvmem-core-generalize-mac-base-cells-handling.patch @@ -0,0 +1,248 @@ +From fd0e523037439520813db7c57df5bd37cdf40f7e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 3 Feb 2025 00:10:18 +0100 +Subject: [PATCH 1/2] nvmem: core: generalize "mac-base" cells handling + +Generalize support of "mac-base" nvmem cells and provide a GPL symbol to +permit also other NVMEM layout driver to parse mac-base cells. + +It's VERY COMMON for some specially formatted NVMEM to expose a mac +address in ASCII format or HEX format hence prevent code duplication by +exposing a common helper. + +Such helper will change the nvmem_info_cell and apply the correct post +process function to correctly parse the mac address. + +Since the API requires OF and is only related to layout, move the +function in layouts.c to correctly handle non-OF NVMEM. + +Signed-off-by: Christian Marangi +--- + drivers/nvmem/core.c | 79 +-------------------------------- + drivers/nvmem/layouts.c | 80 ++++++++++++++++++++++++++++++++++ + include/linux/nvmem-provider.h | 4 ++ + 3 files changed, 86 insertions(+), 77 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -7,12 +7,9 @@ + */ + + #include +-#include +-#include + #include + #include + #include +-#include + #include + #include + #include +@@ -814,62 +811,6 @@ static int nvmem_validate_keepouts(struc + return 0; + } + +-static int nvmem_mac_base_raw_read(void *context, const char *id, int index, unsigned int offset, +- void *buf, size_t bytes) +-{ +- if (WARN_ON(bytes != ETH_ALEN)) +- return -EINVAL; +- +- if (index) +- eth_addr_add(buf, index); +- +- return 0; +-} +- +-static int nvmem_mac_base_ascii_read(void *context, const char *id, int index, unsigned int offset, +- void *buf, size_t bytes) +-{ +- u8 mac[ETH_ALEN]; +- +- if (WARN_ON(bytes != 3 * ETH_ALEN - 1)) +- return -EINVAL; +- +- if (!mac_pton(buf, mac)) +- return -EINVAL; +- +- if (index) +- eth_addr_add(mac, index); +- +- ether_addr_copy(buf, mac); +- +- return 0; +-} +- +-static int nvmem_mac_base_hex_read(void *context, const char *id, int index, unsigned int offset, +- void *buf, size_t bytes) +-{ +- u8 mac[ETH_ALEN], *hexstr; +- int i; +- +- if (WARN_ON(bytes != 2 * ETH_ALEN)) +- return -EINVAL; +- +- hexstr = (u8 *)buf; +- for (i = 0; i < ETH_ALEN; i++) { +- if (!isxdigit(hexstr[i * 2]) || !isxdigit(hexstr[i * 2 + 1])) +- return -EINVAL; +- +- mac[i] = (hex_to_bin(hexstr[i * 2]) << 4) | hex_to_bin(hexstr[i * 2 + 1]); +- } +- +- if (index) +- eth_addr_add(mac, index); +- +- ether_addr_copy(buf, mac); +- +- return 0; +-} +- + static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) + { + struct device *dev = &nvmem->dev; +@@ -911,24 +852,8 @@ static int nvmem_add_cells_from_dt(struc + if (nvmem->fixup_dt_cell_info) + nvmem->fixup_dt_cell_info(nvmem, &info); + +- if (of_device_is_compatible(np, "fixed-layout")) { +- if (of_device_is_compatible(child, "mac-base")) { +- if (info.bytes == ETH_ALEN) { +- info.raw_len = info.bytes; +- info.bytes = ETH_ALEN; +- info.read_post_process = nvmem_mac_base_raw_read; +- } else if (info.bytes == 2 * ETH_ALEN) { +- info.raw_len = info.bytes; +- info.bytes = ETH_ALEN; +- info.read_post_process = nvmem_mac_base_hex_read; +- } else if (info.bytes == 3 * ETH_ALEN - 1) { +- info.raw_len = info.bytes; +- info.bytes = ETH_ALEN; +- info.read_post_process = nvmem_mac_base_ascii_read; +- } +- +- } +- } ++ if (of_device_is_compatible(np, "fixed-layout")) ++ nvmem_layout_parse_mac_base(&info); + + ret = nvmem_add_one_cell(nvmem, &info); + kfree(info.name); +--- a/drivers/nvmem/layouts.c ++++ b/drivers/nvmem/layouts.c +@@ -6,8 +6,11 @@ + * Author: Miquel Raynal + #include + #include ++#include ++#include + #include + #include + #include +@@ -21,6 +24,83 @@ + #define to_nvmem_layout_device(_dev) \ + container_of((_dev), struct nvmem_layout, dev) + ++static int nvmem_mac_base_raw_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ if (WARN_ON(bytes != ETH_ALEN)) ++ return -EINVAL; ++ ++ if (index) ++ eth_addr_add(buf, index); ++ ++ return 0; ++} ++ ++static int nvmem_mac_base_ascii_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ u8 mac[ETH_ALEN]; ++ ++ if (WARN_ON(bytes != 3 * ETH_ALEN - 1)) ++ return -EINVAL; ++ ++ if (!mac_pton(buf, mac)) ++ return -EINVAL; ++ ++ if (index) ++ eth_addr_add(mac, index); ++ ++ ether_addr_copy(buf, mac); ++ ++ return 0; ++} ++ ++static int nvmem_mac_base_hex_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ u8 mac[ETH_ALEN], *hexstr; ++ int i; ++ ++ if (WARN_ON(bytes != 2 * ETH_ALEN)) ++ return -EINVAL; ++ ++ hexstr = (u8 *)buf; ++ for (i = 0; i < ETH_ALEN; i++) { ++ if (!isxdigit(hexstr[i * 2]) || !isxdigit(hexstr[i * 2 + 1])) ++ return -EINVAL; ++ ++ mac[i] = (hex_to_bin(hexstr[i * 2]) << 4) | hex_to_bin(hexstr[i * 2 + 1]); ++ } ++ ++ if (index) ++ eth_addr_add(mac, index); ++ ++ ether_addr_copy(buf, mac); ++ ++ return 0; ++} ++ ++void nvmem_layout_parse_mac_base(struct nvmem_cell_info *info) ++{ ++ if (!of_device_is_compatible(info->np, "mac-base")) ++ return; ++ ++ if (info->bytes == ETH_ALEN) { ++ info->raw_len = info->bytes; ++ info->bytes = ETH_ALEN; ++ info->read_post_process = nvmem_mac_base_raw_read; ++ } else if (info->bytes == 2 * ETH_ALEN) { ++ info->raw_len = info->bytes; ++ info->bytes = ETH_ALEN; ++ info->read_post_process = nvmem_mac_base_hex_read; ++ } else if (info->bytes == 3 * ETH_ALEN - 1) { ++ info->raw_len = info->bytes; ++ info->bytes = ETH_ALEN; ++ info->read_post_process = nvmem_mac_base_ascii_read; ++ } ++} ++EXPORT_SYMBOL_GPL(nvmem_layout_parse_mac_base); ++ + static int nvmem_layout_bus_match(struct device *dev, const struct device_driver *drv) + { + return of_driver_match_device(dev, drv); +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -242,6 +242,8 @@ static inline void nvmem_layout_unregist + + #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) + ++void nvmem_layout_parse_mac_base(struct nvmem_cell_info *info); ++ + /** + * of_nvmem_layout_get_container() - Get OF node of layout container + * +@@ -254,6 +256,8 @@ struct device_node *of_nvmem_layout_get_ + + #else /* CONFIG_NVMEM && CONFIG_OF */ + ++static inline void nvmem_layout_parse_mac_base(struct nvmem_cell_info *info) {} ++ + static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) + { + return NULL; diff --git a/target/linux/generic/pending-6.12/809-02-nvmem-layouts-add-support-for-ascii-env-driver.patch b/target/linux/generic/pending-6.12/809-02-nvmem-layouts-add-support-for-ascii-env-driver.patch new file mode 100644 index 00000000000..0906e0f725f --- /dev/null +++ b/target/linux/generic/pending-6.12/809-02-nvmem-layouts-add-support-for-ascii-env-driver.patch @@ -0,0 +1,204 @@ +From 38287e8ec5c0281377fc70f11f20bcd9986a05f5 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 3 Feb 2025 00:36:12 +0100 +Subject: [PATCH 2/2] nvmem: layouts: add support for ascii-env driver + +Add support for simple ASCII envirorment driver for NVMEM layouts. + +It's very common for devices to store simple text file format in +partition for environment varibles. The most common pattern is variable +name, a delimiter and variable value all separated by a new line +character (\n). + +This driver adds support for exporting such data and expose NVMEM cells +so they can be referenced by other drivers. This driver also supports +parsing mac-base NVMEM cells to parse ASCII or HEX mac address. + +Signed-off-by: Christian Marangi +--- + drivers/nvmem/layouts/Kconfig | 13 +++ + drivers/nvmem/layouts/Makefile | 1 + + drivers/nvmem/layouts/ascii-env.c | 140 ++++++++++++++++++++++++++++++ + 3 files changed, 154 insertions(+) + create mode 100644 drivers/nvmem/layouts/ascii-env.c + +--- a/drivers/nvmem/layouts/Kconfig ++++ b/drivers/nvmem/layouts/Kconfig +@@ -37,6 +37,19 @@ config NVMEM_LAYOUT_U_BOOT_ENV + + If unsure, say N. + ++config NVMEM_LAYOUT_ASCII_ENV ++ tristate "ASCII environment variables layout" ++ help ++ It's very common for devices to store simple text file format in ++ partition for environment varibles. The most common pattern is variable ++ name, a delimiter and variable value all separated by a new line ++ character (\n). ++ This driver adds support for exporting such data and expose NVMEM cells ++ so they can be referenced by other drivers. This driver also supports ++ parsing mac-base NVMEM cells to parse ASCII or HEX mac address. ++ ++ If unsure, say N. ++ + endmenu + + endif +--- a/drivers/nvmem/layouts/Makefile ++++ b/drivers/nvmem/layouts/Makefile +@@ -6,3 +6,4 @@ + obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o + obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o + obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o ++obj-$(CONFIG_NVMEM_LAYOUT_ASCII_ENV) += ascii-env.o +--- /dev/null ++++ b/drivers/nvmem/layouts/ascii-env.c +@@ -0,0 +1,148 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) 2024 Christian Marangi ++ * ++ * This borrow some parse logic from u-boot-env. ++ */ ++#include ++#include ++#include ++#include ++ ++struct ascii_env_match_data { ++ const char delim; ++}; ++ ++/* ++ * Parse a buffer as an ASCII text with name delimiter value and each pattern separated ++ * with a new line char '\n' ++ * Example: (delimiter '=') ++ * name=value\nname2=value2\n ++ * 2 Cell: ++ * - name: value ++ * - name2: value2 ++ */ ++static int ascii_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf, ++ size_t data_len, const char delim) ++{ ++ char *var, *value, *eq, *lf; ++ char *data = buf; ++ ++ /* ++ * Warning the inner loop take care of replacing '\n' ++ * with '\0', hence we can use strlen on value. ++ */ ++ for (var = data; var < data + data_len && *var; ++ var = value + strlen(value) + 1) { ++ struct nvmem_cell_info info = {}; ++ struct device_node *child; ++ const char *label; ++ ++ eq = strchr(var, delim); ++ if (!eq) ++ break; ++ *eq = '\0'; ++ value = eq + 1; ++ ++ /* Replace '\n' with '\0' to use strlen for value */ ++ lf = strchr(value, '\n'); ++ if (!lf) ++ break; ++ *lf = '\0'; ++ ++ info.name = devm_kstrdup(dev, var, GFP_KERNEL); ++ if (!info.name) ++ return -ENOMEM; ++ info.offset = value - data; ++ info.bytes = strlen(value); ++ info.np = of_get_child_by_name(dev->of_node, info.name); ++ for_each_child_of_node(dev->of_node, child) { ++ if (!of_property_read_string(child, "label", &label) && ++ !strncmp(info.name, label, info.bytes)) ++ info.np = child; ++ else if (of_node_name_eq(child, info.name)) ++ info.np = child; ++ } ++ ++ nvmem_layout_parse_mac_base(&info); ++ ++ nvmem_add_one_cell(nvmem, &info); ++ } ++ ++ return 0; ++} ++ ++static int ascii_env_add_cells(struct nvmem_layout *layout) ++{ ++ struct nvmem_device *nvmem = layout->nvmem; ++ const struct ascii_env_match_data *data; ++ struct device *dev = &layout->dev; ++ size_t dev_size; ++ uint8_t *buf; ++ int bytes; ++ int ret; ++ ++ /* Get the delimiter for name value pattern */ ++ data = device_get_match_data(dev); ++ ++ dev_size = nvmem_dev_size(nvmem); ++ ++ buf = kzalloc(dev_size, GFP_KERNEL); ++ if (!buf) { ++ ret = -ENOMEM; ++ goto err_out; ++ } ++ ++ bytes = nvmem_device_read(nvmem, 0, dev_size, buf); ++ if (bytes < 0) { ++ ret = bytes; ++ goto err_kfree; ++ } else if (bytes != dev_size) { ++ ret = -EIO; ++ goto err_kfree; ++ } ++ ++ buf[dev_size - 1] = '\0'; ++ ret = ascii_env_parse_cells(dev, nvmem, buf, dev_size, data->delim); ++ ++err_kfree: ++ kfree(buf); ++err_out: ++ return ret; ++} ++ ++static int ascii_env_probe(struct nvmem_layout *layout) ++{ ++ layout->add_cells = ascii_env_add_cells; ++ ++ return nvmem_layout_register(layout); ++} ++ ++static void ascii_env_remove(struct nvmem_layout *layout) ++{ ++ nvmem_layout_unregister(layout); ++} ++ ++static const struct ascii_env_match_data ascii_env_eq = { ++ .delim = '=', ++}; ++ ++static const struct of_device_id ascii_env_of_match_table[] = { ++ { .compatible = "ascii-eq-delim-env", .data = &ascii_env_eq, }, ++ {}, ++}; ++ ++static struct nvmem_layout_driver ascii_env_layout = { ++ .driver = { ++ .name = "ascii-env-layout", ++ .of_match_table = ascii_env_of_match_table, ++ }, ++ .probe = ascii_env_probe, ++ .remove = ascii_env_remove, ++}; ++module_nvmem_layout_driver(ascii_env_layout); ++ ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); ++MODULE_DEVICE_TABLE(of, ascii_env_of_match_table); ++MODULE_DESCRIPTION("NVMEM layout driver for ASCII environment variables"); diff --git a/target/linux/generic/pending-6.12/809-03-nvmem-layouts-ascii-env-handle-CRLF-while-parsing.patch b/target/linux/generic/pending-6.12/809-03-nvmem-layouts-ascii-env-handle-CRLF-while-parsing.patch new file mode 100644 index 00000000000..ab5fc58d0e9 --- /dev/null +++ b/target/linux/generic/pending-6.12/809-03-nvmem-layouts-ascii-env-handle-CRLF-while-parsing.patch @@ -0,0 +1,58 @@ +From: George Moussalem +Date: Thu, 06 Feb 2025 21:55:28 +0400 +Subject: [PATCH] nvmem: layouts: ascii-env handle CRLF while parsing + +The current driver supports LF line endings only. + +For CRLF-based line endings in the ASCII env, the length of the value of +the variable passed to nvmem_layout_parse_mac_base is 18 bytes instead +of an expected length of 17 causing the parsing to fail. +So, let's add the ability to handle CRLF line endings by adding a +condition to check if the value ends with a '\r' character and replace it +with '\0' to properly parse the mac address. + +Tested on Linksys MX2000, MX5500, and SPNMX56. + +Signed-off-by: George Moussalem +--- +--- a/drivers/nvmem/layouts/ascii-env.c ++++ b/drivers/nvmem/layouts/ascii-env.c +@@ -25,18 +25,20 @@ struct ascii_env_match_data { + static int ascii_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf, + size_t data_len, const char delim) + { +- char *var, *value, *eq, *lf; ++ char *var, *value, *eq, *lf, *cr; + char *data = buf; ++ uint incr = 0; + + /* + * Warning the inner loop take care of replacing '\n' + * with '\0', hence we can use strlen on value. + */ + for (var = data; var < data + data_len && *var; +- var = value + strlen(value) + 1) { ++ var = value + strlen(value) + incr) { + struct nvmem_cell_info info = {}; + struct device_node *child; + const char *label; ++ incr = 0; + + eq = strchr(var, delim); + if (!eq) +@@ -49,6 +51,15 @@ static int ascii_env_parse_cells(struct + if (!lf) + break; + *lf = '\0'; ++ incr++; ++ ++ /* For CRLF based env, replace '\r' with '\0' too to use strlen ++ * for value, and increment var by one in loop for next variable */ ++ cr = strchr(value, '\r'); ++ if (cr) { ++ *cr = '\0'; ++ incr++; ++ } + + info.name = devm_kstrdup(dev, var, GFP_KERNEL); + if (!info.name) diff --git a/target/linux/generic/pending-6.12/810-pci_disable_common_quirks.patch b/target/linux/generic/pending-6.12/810-pci_disable_common_quirks.patch new file mode 100644 index 00000000000..89dcbc27861 --- /dev/null +++ b/target/linux/generic/pending-6.12/810-pci_disable_common_quirks.patch @@ -0,0 +1,62 @@ +From: Gabor Juhos +Subject: debloat: add kernel config option to disabling common PCI quirks + +Signed-off-by: Gabor Juhos +--- + drivers/pci/Kconfig | 6 ++++++ + drivers/pci/quirks.c | 6 ++++++ + 2 files changed, 12 insertions(+) + +--- a/drivers/pci/Kconfig ++++ b/drivers/pci/Kconfig +@@ -118,6 +118,13 @@ config XEN_PCIDEV_FRONTEND + The PCI device frontend driver allows the kernel to import arbitrary + PCI devices from a PCI backend to support PCI driver domains. + ++config PCI_DISABLE_COMMON_QUIRKS ++ bool "PCI disable common quirks" ++ depends on PCI ++ help ++ If you don't know what to do here, say N. ++ ++ + config PCI_ATS + bool + +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -313,6 +313,7 @@ static void quirk_mmio_always_on(struct + DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS + /* + * The Mellanox Tavor device gives false positive parity errors. Disable + * parity error reporting. +@@ -3509,6 +3510,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata); + ++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ + /* + * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum. + * To work around this, query the size it should be configured to by the +@@ -3534,6 +3537,8 @@ static void quirk_intel_ntb(struct pci_d + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++ + /* + * Some BIOS implementations leave the Intel GPU interrupts enabled, even + * though no one is handling them (e.g., if the i915 driver is never +@@ -3572,6 +3577,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq); + ++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ + /* + * PCI devices which are on Intel chips can skip the 10ms delay + * before entering D3 mode. diff --git a/target/linux/generic/pending-6.12/811-pci_disable_usb_common_quirks.patch b/target/linux/generic/pending-6.12/811-pci_disable_usb_common_quirks.patch new file mode 100644 index 00000000000..4e0360d47fe --- /dev/null +++ b/target/linux/generic/pending-6.12/811-pci_disable_usb_common_quirks.patch @@ -0,0 +1,63 @@ +From: Felix Fietkau +Date: Sat, 26 Apr 2025 21:10:40 +0200 +Subject: [PATCH] debloat: disable common USB quirks + +Signed-off-by: Felix Fietkau +--- + drivers/usb/host/pci-quirks.c | 7 +++++++ + drivers/usb/host/pci-quirks.h | 4 ++++ + 2 files changed, 11 insertions(+) + +--- a/drivers/usb/host/pci-quirks.c ++++ b/drivers/usb/host/pci-quirks.c +@@ -590,6 +590,7 @@ bool usb_amd_pt_check_port(struct device + EXPORT_SYMBOL_GPL(usb_amd_pt_check_port); + #endif /* CONFIG_USB_PCI_AMD */ + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS + static int usb_asmedia_wait_write(struct pci_dev *pdev) + { + unsigned long retry_count; +@@ -642,6 +643,7 @@ static inline int io_type_enabled(struct + } + + #define mmio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_MEMORY) ++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */ + + #if defined(CONFIG_HAS_IOPORT) && IS_ENABLED(CONFIG_USB_UHCI_HCD) + /* +@@ -724,6 +726,10 @@ reset_needed: + return 1; + } + EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc); ++#endif ++ ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++#if defined(CONFIG_HAS_IOPORT) && IS_ENABLED(CONFIG_USB_UHCI_HCD) + + #define pio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_IO) + +@@ -1304,3 +1310,4 @@ static void quirk_usb_early_handoff(stru + } + DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff); ++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */ +--- a/drivers/usb/host/pci-quirks.h ++++ b/drivers/usb/host/pci-quirks.h +@@ -38,12 +38,16 @@ static inline bool usb_amd_pt_check_port + #ifdef CONFIG_USB_PCI + void uhci_reset_hc(struct pci_dev *pdev, unsigned long base); + int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base); ++#endif ++ ++#if defined(CONFIG_USB_PCI) && !defined(CONFIG_PCI_DISABLE_COMMON_QUIRKS) + void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev); + void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev); + void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); + #else + struct pci_dev; + static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {} ++static inline void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) {} + static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {} + #endif /* CONFIG_USB_PCI */ + diff --git a/target/linux/generic/pending-6.12/812-PCI-sysfs-enforce-single-creation-of-sysfs-entry-for.patch b/target/linux/generic/pending-6.12/812-PCI-sysfs-enforce-single-creation-of-sysfs-entry-for.patch new file mode 100644 index 00000000000..e5f54d7214e --- /dev/null +++ b/target/linux/generic/pending-6.12/812-PCI-sysfs-enforce-single-creation-of-sysfs-entry-for.patch @@ -0,0 +1,142 @@ +From d33941523a8379e30070374b133b28a2077dcef8 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 13 Oct 2025 20:45:25 +0200 +Subject: [PATCH] PCI/sysfs: enforce single creation of sysfs entry for pdev +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In some specific scenario it's possible that the +pci_create_resource_files() gets called multiple times and the created +entry actually gets wrongly deleted with extreme case of having a NULL +pointer dereference when the PCI is removed. + +This mainly happen due to bad timing where the PCI bus is adding PCI +devices and at the same time the sysfs code is adding the entry causing +double execution of the pci_create_resource_files function and kernel +WARNING. + +To be more precise there is a race between the late_initcall of +pci-sysfs with pci_sysfs_init and PCI bus.c pci_bus_add_device that also +call pci_create_sysfs_dev_files. + +With correct amount of ""luck"" (or better say bad luck) +pci_create_sysfs_dev_files in bus.c might be called with pci_sysfs_init +is executing the loop. + +This has been reported multiple times and on multiple system, like imx6 +system, ipq806x systems... + +To address this, imlement multiple improvement to the implementation: +1. Add a bool to pci_dev to flag when sysfs entry are created + (sysfs_init) +2. Implement a simple completion to wait pci_sysfs_init execution. +3. Permit additional call of pci_create_sysfs_dev_files only after + pci_sysfs_init has finished. + +With such logic in place, we address al kind of timing problem with +minimal change to any driver. + +A notice worth to mention is that the remove function are not affected +by this as the pci_remove_resource_files have enough check in place to +always work and it's always called by pci_stop_dev. + +Cc: stable@vger.kernel.org +Reported-by: Krzysztof Hałasa +Closes: https://bugzilla.kernel.org/show_bug.cgi?id=215515 +Signed-off-by: Christian Marangi +--- + drivers/pci/pci-sysfs.c | 34 +++++++++++++++++++++++++++++----- + include/linux/pci.h | 1 + + 2 files changed, 30 insertions(+), 5 deletions(-) + +--- a/drivers/pci/pci-sysfs.c ++++ b/drivers/pci/pci-sysfs.c +@@ -13,6 +13,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -36,6 +37,7 @@ + #endif + + static int sysfs_initialized; /* = 0 */ ++static DECLARE_COMPLETION(sysfs_init_completion); + + /* show configuration fields */ + #define pci_config_attr(field, format_string) \ +@@ -1551,12 +1553,32 @@ static const struct attribute_group pci_ + .is_visible = resource_resize_is_visible, + }; + ++static int __pci_create_sysfs_dev_files(struct pci_dev *pdev) ++{ ++ int ret; ++ ++ ret = pci_create_resource_files(pdev); ++ if (ret) ++ return ret; ++ ++ /* on success set sysfs correctly created */ ++ pdev->sysfs_init = true; ++ return 0; ++} ++ + int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev) + { + if (!sysfs_initialized) + return -EACCES; + +- return pci_create_resource_files(pdev); ++ /* sysfs entry already created */ ++ if (pdev->sysfs_init) ++ return 0; ++ ++ /* wait for pci_sysfs_init */ ++ wait_for_completion(&sysfs_init_completion); ++ ++ return __pci_create_sysfs_dev_files(pdev); + } + + /** +@@ -1577,21 +1599,23 @@ static int __init pci_sysfs_init(void) + { + struct pci_dev *pdev = NULL; + struct pci_bus *pbus = NULL; +- int retval; ++ int retval = 0; + + sysfs_initialized = 1; + for_each_pci_dev(pdev) { +- retval = pci_create_sysfs_dev_files(pdev); ++ retval = __pci_create_sysfs_dev_files(pdev); + if (retval) { + pci_dev_put(pdev); +- return retval; ++ goto exit; + } + } + + while ((pbus = pci_find_next_bus(pbus))) + pci_create_legacy_files(pbus); + +- return 0; ++exit: ++ complete_all(&sysfs_init_completion); ++ return retval; + } + late_initcall(pci_sysfs_init); + +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -484,6 +484,7 @@ struct pci_dev { + unsigned int rom_attr_enabled:1; /* Display of ROM attribute enabled? */ + pci_dev_flags_t dev_flags; + atomic_t enable_cnt; /* pci_enable_device has been called */ ++ bool sysfs_init; /* sysfs entry has been created */ + + spinlock_t pcie_cap_lock; /* Protects RMW ops in capability accessors */ + u32 saved_config_space[16]; /* Config space saved at suspend time */ diff --git a/target/linux/generic/pending-6.12/834-ledtrig-libata.patch b/target/linux/generic/pending-6.12/834-ledtrig-libata.patch new file mode 100644 index 00000000000..4b497d9fbf8 --- /dev/null +++ b/target/linux/generic/pending-6.12/834-ledtrig-libata.patch @@ -0,0 +1,145 @@ +From: Daniel Golle +Subject: libata: add ledtrig support + +This adds a LED trigger for each ATA port indicating disk activity. + +As this is needed only on specific platforms (NAS SoCs and such), +these platforms should define ARCH_WANTS_LIBATA_LEDS if there +are boards with LED(s) intended to indicate ATA disk activity and +need the OS to take care of that. +In that way, if not selected, LED trigger support not will be +included in libata-core and both, codepaths and structures remain +untouched. + +Signed-off-by: Daniel Golle +--- + drivers/ata/Kconfig | 16 ++++++++++++++++ + drivers/ata/libata-core.c | 41 +++++++++++++++++++++++++++++++++++++++++ + include/linux/libata.h | 9 +++++++++ + 3 files changed, 66 insertions(+) + +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -67,6 +67,22 @@ config ATA_FORCE + + If unsure, say Y. + ++config ARCH_WANT_LIBATA_LEDS ++ bool ++ ++config ATA_LEDS ++ bool "support ATA port LED triggers" ++ depends on ARCH_WANT_LIBATA_LEDS ++ select NEW_LEDS ++ select LEDS_CLASS ++ select LEDS_TRIGGERS ++ default y ++ help ++ This option adds a LED trigger for each registered ATA port. ++ It is used to drive disk activity leds connected via GPIO. ++ ++ If unsure, say N. ++ + config ATA_ACPI + bool "ATA ACPI Support" + depends on ACPI +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -703,6 +703,17 @@ static inline void ata_set_tf_cdl(struct + qc->flags |= ATA_QCFLAG_HAS_CDL | ATA_QCFLAG_RESULT_TF; + } + ++#ifdef CONFIG_ATA_LEDS ++#define LIBATA_BLINK_DELAY 20 /* ms */ ++static inline void ata_led_act(struct ata_port *ap) ++{ ++ if (unlikely(!ap->ledtrig)) ++ return; ++ ++ led_trigger_blink_oneshot(ap->ledtrig, LIBATA_BLINK_DELAY, LIBATA_BLINK_DELAY, 0); ++} ++#endif ++ + /** + * ata_build_rw_tf - Build ATA taskfile for given read/write request + * @qc: Metadata associated with the taskfile to build +@@ -4807,6 +4818,9 @@ void __ata_qc_complete(struct ata_queued + link->active_tag = ATA_TAG_POISON; + ap->nr_active_links--; + } ++#ifdef CONFIG_ATA_LEDS ++ ata_led_act(ap); ++#endif + + /* clear exclusive status */ + if (unlikely(qc->flags & ATA_QCFLAG_CLEAR_EXCL && +@@ -5536,6 +5550,9 @@ struct ata_port *ata_port_alloc(struct a + ap->stats.unhandled_irq = 1; + ap->stats.idle_irq = 1; + #endif ++#ifdef CONFIG_ATA_LEDS ++ ap->ledtrig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); ++#endif + ata_sff_port_init(ap); + + ata_force_pflags(ap); +@@ -5552,6 +5569,12 @@ void ata_port_free(struct ata_port *ap) + kfree(ap->pmp_link); + kfree(ap->slave_link); + ida_free(&ata_ida, ap->print_id); ++#ifdef CONFIG_ATA_LEDS ++ if (ap->ledtrig) { ++ led_trigger_unregister(ap->ledtrig); ++ kfree(ap->ledtrig); ++ }; ++#endif + kfree(ap); + } + EXPORT_SYMBOL_GPL(ata_port_free); +@@ -5956,7 +5979,23 @@ int ata_host_register(struct ata_host *h + WARN_ON(1); + return -EINVAL; + } ++#ifdef CONFIG_ATA_LEDS ++ for (i = 0; i < host->n_ports; i++) { ++ if (unlikely(!host->ports[i]->ledtrig)) ++ continue; + ++ snprintf(host->ports[i]->ledtrig_name, ++ sizeof(host->ports[i]->ledtrig_name), "ata%u", ++ host->ports[i]->print_id); ++ ++ host->ports[i]->ledtrig->name = host->ports[i]->ledtrig_name; ++ ++ if (led_trigger_register(host->ports[i]->ledtrig)) { ++ kfree(host->ports[i]->ledtrig); ++ host->ports[i]->ledtrig = NULL; ++ } ++ } ++#endif + /* Create associated sysfs transport objects */ + for (i = 0; i < host->n_ports; i++) { + rc = ata_tport_add(host->dev,host->ports[i]); +--- a/include/linux/libata.h ++++ b/include/linux/libata.h +@@ -23,6 +23,9 @@ + #include + #include + #include ++#ifdef CONFIG_ATA_LEDS ++#include ++#endif + + /* + * Define if arch has non-standard setup. This is a _PCI_ standard +@@ -937,6 +940,10 @@ struct ata_port { + #ifdef CONFIG_ATA_ACPI + struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */ + #endif ++#ifdef CONFIG_ATA_LEDS ++ struct led_trigger *ledtrig; ++ char ledtrig_name[8]; ++#endif + }; + + /* The following initializer overrides a method to NULL whether one of diff --git a/target/linux/generic/pending-6.12/840-hwrng-bcm2835-set-quality-to-1000.patch b/target/linux/generic/pending-6.12/840-hwrng-bcm2835-set-quality-to-1000.patch new file mode 100644 index 00000000000..3172ad5a167 --- /dev/null +++ b/target/linux/generic/pending-6.12/840-hwrng-bcm2835-set-quality-to-1000.patch @@ -0,0 +1,26 @@ +From d6988cf1d16faac56899918bb2b1be8d85155e3f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Sat, 20 Feb 2021 18:36:38 +0100 +Subject: [PATCH] hwrng: bcm2835: set quality to 1000 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This allows devices without a high precission timer to reduce boot from >100s +to <30s. + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/char/hw_random/bcm2835-rng.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/char/hw_random/bcm2835-rng.c ++++ b/drivers/char/hw_random/bcm2835-rng.c +@@ -169,6 +169,7 @@ static int bcm2835_rng_probe(struct plat + priv->rng.init = bcm2835_rng_init; + priv->rng.read = bcm2835_rng_read; + priv->rng.cleanup = bcm2835_rng_cleanup; ++ priv->rng.quality = 1000; + + if (dev_of_node(dev)) { + rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node); diff --git a/target/linux/generic/pending-6.12/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch b/target/linux/generic/pending-6.12/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch new file mode 100644 index 00000000000..6a90959f619 --- /dev/null +++ b/target/linux/generic/pending-6.12/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch @@ -0,0 +1,102 @@ +From 663b9f99bb35dbc0c7b685f71ee3668a60d31320 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Mon, 10 Jan 2022 02:02:00 +0100 +Subject: [PATCH] PCI: aardvark: Make main irq_chip structure a static driver + structure +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Marc Zyngier says [1] that we should use struct irq_chip as a global +static struct in the driver. Even though the structure currently +contains a dynamic member (parent_device), Marc says [2] that he plans +to kill it and make the structure completely static. + +We have already converted others irq_chip structures in this driver in +this way, but we omitted this one because the .name member is +dynamically created from device's name, and the name is displayed in +sysfs, so changing it would break sysfs ABI. + +The rationale for changing the name (to "advk-INT") in spite of sysfs +ABI, and thus allowing to convert to a static structure, is that after +the other changes we made in this series, the IRQ chip is basically +something different: it no logner generates ERR and PME interrupts (they +are generated by emulated bridge's rp_irq_chip). + +[1] https://lore.kernel.org/linux-pci/877dbcvngf.wl-maz@kernel.org/ +[2] https://lore.kernel.org/linux-pci/874k6gvkhz.wl-maz@kernel.org/ + +Signed-off-by: Marek Behún +--- + drivers/pci/controller/pci-aardvark.c | 25 +++++++------------------ + 1 file changed, 7 insertions(+), 18 deletions(-) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -276,7 +276,6 @@ struct advk_pcie { + u8 wins_count; + struct irq_domain *rp_irq_domain; + struct irq_domain *irq_domain; +- struct irq_chip irq_chip; + raw_spinlock_t irq_lock; + struct irq_domain *msi_domain; + struct irq_domain *msi_inner_domain; +@@ -1418,14 +1417,19 @@ static void advk_pcie_irq_unmask(struct + raw_spin_unlock_irqrestore(&pcie->irq_lock, flags); + } + ++static struct irq_chip advk_irq_chip = { ++ .name = "advk-INT", ++ .irq_mask = advk_pcie_irq_mask, ++ .irq_unmask = advk_pcie_irq_unmask, ++}; ++ + static int advk_pcie_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hwirq) + { + struct advk_pcie *pcie = h->host_data; + + irq_set_status_flags(virq, IRQ_LEVEL); +- irq_set_chip_and_handler(virq, &pcie->irq_chip, +- handle_level_irq); ++ irq_set_chip_and_handler(virq, &advk_irq_chip, handle_level_irq); + irq_set_chip_data(virq, pcie); + + return 0; +@@ -1485,7 +1489,6 @@ static int advk_pcie_init_irq_domain(str + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; +- struct irq_chip *irq_chip; + int ret = 0; + + raw_spin_lock_init(&pcie->irq_lock); +@@ -1496,28 +1499,14 @@ static int advk_pcie_init_irq_domain(str + return -ENODEV; + } + +- irq_chip = &pcie->irq_chip; +- +- irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq", +- dev_name(dev)); +- if (!irq_chip->name) { +- ret = -ENOMEM; +- goto out_put_node; +- } +- +- irq_chip->irq_mask = advk_pcie_irq_mask; +- irq_chip->irq_unmask = advk_pcie_irq_unmask; +- + pcie->irq_domain = + irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &advk_pcie_irq_domain_ops, pcie); + if (!pcie->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + ret = -ENOMEM; +- goto out_put_node; + } + +-out_put_node: + of_node_put(pcie_intc_node); + return ret; + } diff --git a/target/linux/generic/pending-6.12/890-usb-serial-add-support-for-CH348.patch b/target/linux/generic/pending-6.12/890-usb-serial-add-support-for-CH348.patch new file mode 100644 index 00000000000..028ecf41f7e --- /dev/null +++ b/target/linux/generic/pending-6.12/890-usb-serial-add-support-for-CH348.patch @@ -0,0 +1,783 @@ +From df1357358eec062241bddd2995e7ef0ce86cf45a Mon Sep 17 00:00:00 2001 +X-Patchwork-Submitter: Corentin Labbe +X-Patchwork-Id: 13656881 +Message-Id: <20240507131522.3546113-2-clabbe@baylibre.com> +X-Mailer: git-send-email 2.25.1 +In-Reply-To: <20240507131522.3546113-1-clabbe@baylibre.com> +References: <20240507131522.3546113-1-clabbe@baylibre.com> +Precedence: bulk +X-Mailing-List: linux-usb@vger.kernel.org +List-Id: +From: Corentin Labbe +Date: Tue, 7 May 2024 13:15:22 +0000 +Subject: [PATCH v7] usb: serial: add support for CH348 + +The CH348 is an USB octo port serial adapter. +The device multiplexes all 8 ports in the same pair of Bulk endpoints. +Since there is no public datasheet, unfortunately it remains some magic values + +Signed-off-by: Corentin Labbe +Tested-by: Martin Blumenstingl +--- + drivers/usb/serial/Kconfig | 9 + + drivers/usb/serial/Makefile | 1 + + drivers/usb/serial/ch348.c | 725 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 735 insertions(+) + create mode 100644 drivers/usb/serial/ch348.c + +--- a/drivers/usb/serial/Kconfig ++++ b/drivers/usb/serial/Kconfig +@@ -112,6 +112,15 @@ config USB_SERIAL_CH341 + To compile this driver as a module, choose M here: the + module will be called ch341. + ++config USB_SERIAL_CH348 ++ tristate "USB Winchiphead CH348 Octo Port Serial Driver" ++ help ++ Say Y here if you want to use a Winchiphead CH348 octo port ++ USB to serial adapter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ch348. ++ + config USB_SERIAL_WHITEHEAT + tristate "USB ConnectTech WhiteHEAT Serial Driver" + select USB_EZUSB_FX2 +--- a/drivers/usb/serial/Makefile ++++ b/drivers/usb/serial/Makefile +@@ -15,6 +15,7 @@ obj-$(CONFIG_USB_SERIAL_AIRCABLE) += ai + obj-$(CONFIG_USB_SERIAL_ARK3116) += ark3116.o + obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o + obj-$(CONFIG_USB_SERIAL_CH341) += ch341.o ++obj-$(CONFIG_USB_SERIAL_CH348) += ch348.o + obj-$(CONFIG_USB_SERIAL_CP210X) += cp210x.o + obj-$(CONFIG_USB_SERIAL_CYBERJACK) += cyberjack.o + obj-$(CONFIG_USB_SERIAL_CYPRESS_M8) += cypress_m8.o +--- /dev/null ++++ b/drivers/usb/serial/ch348.c +@@ -0,0 +1,725 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * USB serial driver for USB to Octal UARTs chip ch348. ++ * ++ * Copyright (C) 2022 Corentin Labbe ++ * With the help of Neil Armstrong ++ * and the help of Martin Blumenstingl ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CH348_CMD_TIMEOUT 2000 ++ ++#define CH348_CTO_D 0x01 ++#define CH348_CTO_R 0x02 ++ ++#define CH348_CTI_C 0x10 ++#define CH348_CTI_DSR 0x20 ++#define CH348_CTI_R 0x40 ++#define CH348_CTI_DCD 0x80 ++ ++#define CH348_LO 0x02 ++#define CH348_LP 0x04 ++#define CH348_LF 0x08 ++#define CH348_LB 0x10 ++ ++#define CMD_W_R 0xC0 ++#define CMD_W_BR 0x80 ++ ++#define CMD_WB_E 0x90 ++#define CMD_RB_E 0xC0 ++ ++#define M_NOR 0x00 ++#define M_HF 0x03 ++ ++#define R_MOD 0x97 ++#define R_IO_D 0x98 ++#define R_IO_O 0x99 ++#define R_IO_I 0x9b ++#define R_TM_O 0x9c ++#define R_INIT 0xa1 ++ ++#define R_C1 0x01 ++#define R_C2 0x02 ++#define R_C4 0x04 ++#define R_C5 0x06 ++ ++#define R_II_B1 0x06 ++#define R_II_B2 0x02 ++#define R_II_B3 0x00 ++ ++#define CMD_VER 0x96 ++ ++#define CH348_RX_PORT_CHUNK_LENGTH 32 ++#define CH348_RX_PORT_MAX_LENGTH 30 ++ ++struct ch348_rxbuf { ++ u8 port; ++ u8 length; ++ u8 data[CH348_RX_PORT_MAX_LENGTH]; ++} __packed; ++ ++struct ch348_txbuf { ++ u8 port; ++ __le16 length; ++ u8 data[]; ++} __packed; ++ ++#define CH348_TX_HDRSIZE offsetof(struct ch348_txbuf, data) ++ ++struct ch348_initbuf { ++ u8 cmd; ++ u8 reg; ++ u8 port; ++ __be32 baudrate; ++ u8 format; ++ u8 paritytype; ++ u8 databits; ++ u8 rate; ++ u8 unknown; ++} __packed; ++ ++#define CH348_MAXPORT 8 ++ ++/* ++ * The CH348 multiplexes rx & tx into a pair of Bulk USB endpoints for ++ * the 8 serial ports, and another pair of Bulk USB endpoints to ++ * set port settings and receive port status events. ++ * ++ * The USB serial cores ties every Bulk endpoints pairs to each ports, ++ * but in our case it will set port 0 with the rx/tx endpoints ++ * and port 1 with the setup/status endpoints. ++ * ++ * To still take advantage of the generic code, we (re-)initialize ++ * the USB serial port structure with the correct USB endpoint ++ * for read and write, and write proper process_read_urb() ++ * and prepare_write_buffer() to correctly (de-)multiplex data. ++ * Also we use a custom write() implementation to wait until the buffer ++ * has been fully transmitted to prevent TX buffer overruns. ++ */ ++ ++/* ++ * struct ch348_port - per-port information ++ * @uartmode: UART port current mode ++ * @write_completion: completion event when the TX buffer has been written out ++ */ ++struct ch348_port { ++ u8 uartmode; ++ struct completion write_completion; ++}; ++ ++/* ++ * struct ch348 - main container for all this driver information ++ * @udev: pointer to the CH348 USB device ++ * @ports: List of per-port information ++ * @serial: pointer to the serial structure ++ * @write_lock: protect against concurrent writes so we don't lose data ++ * @cmd_ep: endpoint number for configure operations ++ * @status_urb: URB for status ++ * @status_buffer: buffer used by status_urb ++ */ ++struct ch348 { ++ struct usb_device *udev; ++ struct ch348_port ports[CH348_MAXPORT]; ++ struct usb_serial *serial; ++ ++ struct mutex write_lock; ++ ++ int cmd_ep; ++ ++ struct urb *status_urb; ++ u8 status_buffer[]; ++}; ++ ++struct ch348_magic { ++ u8 action; ++ u8 reg; ++ u8 control; ++} __packed; ++ ++struct ch348_status_entry { ++ u8 portnum:4; ++ u8 unused:4; ++ u8 reg_iir; ++ union { ++ u8 lsr_signal; ++ u8 modem_signal; ++ u8 init_data[10]; ++ }; ++} __packed; ++ ++static void ch348_process_status_urb(struct urb *urb) ++{ ++ struct ch348_status_entry *status_entry; ++ struct ch348 *ch348 = urb->context; ++ int ret, status = urb->status; ++ struct usb_serial_port *port; ++ unsigned int i, status_len; ++ ++ switch (status) { ++ case 0: ++ /* success */ ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ /* this urb is terminated, clean up */ ++ dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", ++ __func__, status); ++ return; ++ default: ++ dev_err(&urb->dev->dev, "%s - nonzero urb status received: %d\n", ++ __func__, status); ++ goto exit; ++ } ++ ++ if (urb->actual_length < 3) { ++ dev_warn(&ch348->udev->dev, ++ "Received too short status buffer with %u bytes\n", ++ urb->actual_length); ++ goto exit; ++ } ++ ++ for (i = 0; i < urb->actual_length;) { ++ status_entry = urb->transfer_buffer + i; ++ ++ if (status_entry->portnum >= CH348_MAXPORT) { ++ dev_warn(&ch348->udev->dev, ++ "Invalid port %d in status entry\n", ++ status_entry->portnum); ++ break; ++ } ++ ++ port = ch348->serial->port[status_entry->portnum]; ++ status_len = 3; ++ ++ if (!status_entry->reg_iir) { ++ dev_dbg(&port->dev, "Ignoring status with zero reg_iir\n"); ++ } else if (status_entry->reg_iir == R_INIT) { ++ status_len = 12; ++ } else if ((status_entry->reg_iir & 0x0f) == R_II_B1) { ++ if (status_entry->lsr_signal & CH348_LO) ++ port->icount.overrun++; ++ if (status_entry->lsr_signal & CH348_LP) ++ port->icount.parity++; ++ if (status_entry->lsr_signal & CH348_LF) ++ port->icount.frame++; ++ if (status_entry->lsr_signal & CH348_LF) ++ port->icount.brk++; ++ } else if ((status_entry->reg_iir & 0x0f) == R_II_B2) { ++ complete_all(&ch348->ports[status_entry->portnum].write_completion); ++ } else { ++ dev_warn(&port->dev, ++ "Unsupported status with reg_iir 0x%02x\n", ++ status_entry->reg_iir); ++ } ++ ++ usb_serial_debug_data(&port->dev, __func__, status_len, ++ urb->transfer_buffer + i); ++ ++ i += status_len; ++ } ++ ++exit: ++ ret = usb_submit_urb(urb, GFP_ATOMIC); ++ if (ret) ++ dev_err(&urb->dev->dev, "%s - usb_submit_urb failed; %d\n", ++ __func__, ret); ++} ++ ++/* ++ * Some values came from vendor tree, and we have no meaning for them, this ++ * function simply use them. ++ */ ++static int ch348_do_magic(struct ch348 *ch348, int portnum, u8 action, u8 reg, u8 control) ++{ ++ struct ch348_magic *buffer; ++ int ret, len; ++ ++ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); ++ if (!buffer) ++ return -ENOMEM; ++ ++ if (portnum < 4) ++ reg += 0x10 * portnum; ++ else ++ reg += 0x10 * (portnum - 4) + 0x08; ++ ++ buffer->action = action; ++ buffer->reg = reg; ++ buffer->control = control; ++ ++ ret = usb_bulk_msg(ch348->udev, ch348->cmd_ep, buffer, 3, &len, ++ CH348_CMD_TIMEOUT); ++ if (ret) ++ dev_err(&ch348->udev->dev, "Failed to write magic err=%d\n", ret); ++ ++ kfree(buffer); ++ ++ return ret; ++} ++ ++static int ch348_configure(struct ch348 *ch348, int portnum) ++{ ++ int ret; ++ ++ ret = ch348_do_magic(ch348, portnum, CMD_W_R, R_C2, 0x87); ++ if (ret) ++ return ret; ++ ++ return ch348_do_magic(ch348, portnum, CMD_W_R, R_C4, 0x08); ++} ++ ++static void ch348_process_read_urb(struct urb *urb) ++{ ++ struct usb_serial_port *port = urb->context; ++ struct ch348 *ch348 = usb_get_serial_data(port->serial); ++ unsigned int portnum, usblen, i; ++ struct ch348_rxbuf *rxb; ++ ++ if (urb->actual_length < 2) { ++ dev_dbg(&ch348->udev->dev, "Empty rx buffer\n"); ++ return; ++ } ++ ++ for (i = 0; i < urb->actual_length; i += CH348_RX_PORT_CHUNK_LENGTH) { ++ rxb = urb->transfer_buffer + i; ++ portnum = rxb->port; ++ if (portnum >= CH348_MAXPORT) { ++ dev_dbg(&ch348->udev->dev, "Invalid port %d\n", portnum); ++ break; ++ } ++ ++ port = ch348->serial->port[portnum]; ++ ++ usblen = rxb->length; ++ if (usblen > CH348_RX_PORT_MAX_LENGTH) { ++ dev_dbg(&port->dev, "Invalid length %d for port %d\n", ++ usblen, portnum); ++ break; ++ } ++ ++ tty_insert_flip_string(&port->port, rxb->data, usblen); ++ tty_flip_buffer_push(&port->port); ++ port->icount.rx += usblen; ++ usb_serial_debug_data(&port->dev, __func__, usblen, rxb->data); ++ } ++} ++ ++static int ch348_prepare_write_buffer(struct usb_serial_port *port, void *dest, size_t size) ++{ ++ struct ch348_txbuf *rxt = dest; ++ int count; ++ ++ count = kfifo_out_locked(&port->write_fifo, rxt->data, ++ size - CH348_TX_HDRSIZE, &port->lock); ++ ++ rxt->port = port->port_number; ++ rxt->length = cpu_to_le16(count); ++ ++ return count + CH348_TX_HDRSIZE; ++} ++ ++static int ch348_write(struct tty_struct *tty, struct usb_serial_port *port, ++ const unsigned char *buf, int count) ++{ ++ struct ch348 *ch348 = usb_get_serial_data(port->serial); ++ struct ch348_port *ch348_port = &ch348->ports[port->port_number]; ++ int ret, max_tx_size; ++ ++ if (tty_get_baud_rate(tty) < 9600 && count >= 128) ++ /* ++ * Writing larger buffers can take longer than the hardware ++ * allows before discarding the write buffer. Limit the ++ * transfer size in such cases. ++ * These values have been found by empirical testing. ++ */ ++ max_tx_size = 128; ++ else ++ /* ++ * Only ingest as many bytes as we can transfer with one URB at ++ * a time. Once an URB has been written we need to wait for the ++ * R_II_B2 status event before we are allowed to send more data. ++ * If we ingest more data then usb_serial_generic_write() will ++ * internally try to process as much data as possible with any ++ * number of URBs without giving us the chance to wait in ++ * between transfers. ++ */ ++ max_tx_size = port->bulk_out_size - CH348_TX_HDRSIZE; ++ ++ reinit_completion(&ch348_port->write_completion); ++ ++ mutex_lock(&ch348->write_lock); ++ ++ /* ++ * For any (remaining) bytes that we did not transfer TTY core will ++ * call us again, with the buffer and count adjusted to the remaining ++ * data. ++ */ ++ ret = usb_serial_generic_write(tty, port, buf, min(count, max_tx_size)); ++ ++ mutex_unlock(&ch348->write_lock); ++ ++ if (ret <= 0) ++ return ret; ++ ++ if (!wait_for_completion_interruptible_timeout(&ch348_port->write_completion, ++ msecs_to_jiffies(CH348_CMD_TIMEOUT))) { ++ dev_err_console(port, "Failed to wait for TX buffer flush\n"); ++ return -ETIMEDOUT; ++ } ++ ++ return ret; ++} ++ ++static int ch348_set_uartmode(struct ch348 *ch348, int portnum, u8 mode) ++{ ++ int ret; ++ ++ if (ch348->ports[portnum].uartmode == M_NOR && mode == M_HF) { ++ ret = ch348_do_magic(ch348, portnum, CMD_W_BR, R_C4, 0x51); ++ if (ret) ++ return ret; ++ ch348->ports[portnum].uartmode = M_HF; ++ } ++ ++ if (ch348->ports[portnum].uartmode == M_HF && mode == M_NOR) { ++ ret = ch348_do_magic(ch348, portnum, CMD_W_BR, R_C4, 0x50); ++ if (ret) ++ return ret; ++ ch348->ports[portnum].uartmode = M_NOR; ++ } ++ return 0; ++} ++ ++static void ch348_set_termios(struct tty_struct *tty, struct usb_serial_port *port, ++ const struct ktermios *termios_old) ++{ ++ struct ch348 *ch348 = usb_get_serial_data(port->serial); ++ struct ktermios *termios = &tty->termios; ++ int ret, portnum = port->port_number; ++ struct ch348_initbuf *buffer; ++ speed_t baudrate; ++ u8 format; ++ ++ if (termios_old && !tty_termios_hw_change(&tty->termios, termios_old)) ++ return; ++ ++ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); ++ if (!buffer) { ++ if (termios_old) ++ tty->termios = *termios_old; ++ return; ++ } ++ ++ /* ++ * The datasheet states that only baud rates in range of 1200..6000000 ++ * are supported. Tests however show that even baud rates as low as 50 ++ * and as high as 12000000 are working in practice. ++ */ ++ baudrate = clamp(tty_get_baud_rate(tty), 50, 12000000); ++ ++ format = termios->c_cflag & CSTOPB ? 2 : 1; ++ ++ buffer->paritytype = 0; ++ if (termios->c_cflag & PARENB) { ++ if (termios->c_cflag & PARODD) ++ buffer->paritytype += 1; ++ else ++ buffer->paritytype += 2; ++ if (termios->c_cflag & CMSPAR) ++ buffer->paritytype += 2; ++ } ++ ++ switch (C_CSIZE(tty)) { ++ case CS5: ++ buffer->databits = 5; ++ break; ++ case CS6: ++ buffer->databits = 6; ++ break; ++ case CS7: ++ buffer->databits = 7; ++ break; ++ case CS8: ++ default: ++ buffer->databits = 8; ++ break; ++ } ++ buffer->cmd = CMD_WB_E | (portnum & 0x0F); ++ buffer->reg = R_INIT; ++ buffer->port = portnum; ++ buffer->baudrate = cpu_to_be32(baudrate); ++ ++ if (format == 2) ++ buffer->format = 0x02; ++ else if (format == 1) ++ buffer->format = 0x00; ++ ++ buffer->rate = max_t(speed_t, 5, (10000 * 15 / baudrate) + 1); ++ ++ ret = usb_bulk_msg(ch348->udev, ch348->cmd_ep, buffer, ++ sizeof(*buffer), NULL, CH348_CMD_TIMEOUT); ++ if (ret < 0) { ++ dev_err(&ch348->udev->dev, "Failed to change line settings: err=%d\n", ++ ret); ++ goto out; ++ } ++ ++ ret = ch348_do_magic(ch348, portnum, CMD_W_R, R_C1, 0x0F); ++ if (ret < 0) ++ goto out; ++ ++ if (C_CRTSCTS(tty)) ++ ret = ch348_set_uartmode(ch348, portnum, M_HF); ++ else ++ ret = ch348_set_uartmode(ch348, portnum, M_NOR); ++ ++out: ++ kfree(buffer); ++} ++ ++static int ch348_open(struct tty_struct *tty, struct usb_serial_port *port) ++{ ++ struct ch348 *ch348 = usb_get_serial_data(port->serial); ++ int ret; ++ ++ if (tty) ++ ch348_set_termios(tty, port, NULL); ++ ++ ret = ch348_configure(ch348, port->port_number); ++ if (ret) { ++ dev_err(&ch348->udev->dev, "Fail to configure err=%d\n", ret); ++ return ret; ++ } ++ ++ return usb_serial_generic_open(tty, port); ++} ++ ++static int ch348_attach(struct usb_serial *serial) ++{ ++ struct usb_endpoint_descriptor *epcmd, *epstatus; ++ struct usb_serial_port *port0 = serial->port[1]; ++ struct usb_device *usb_dev = serial->dev; ++ int status_buffer_size, i, ret; ++ struct usb_interface *intf; ++ struct ch348 *ch348; ++ ++ intf = usb_ifnum_to_if(usb_dev, 0); ++ epstatus = &intf->cur_altsetting->endpoint[2].desc; ++ epcmd = &intf->cur_altsetting->endpoint[3].desc; ++ ++ status_buffer_size = usb_endpoint_maxp(epstatus); ++ ++ ch348 = kzalloc(struct_size(ch348, status_buffer, status_buffer_size), ++ GFP_KERNEL); ++ if (!ch348) ++ return -ENOMEM; ++ ++ usb_set_serial_data(serial, ch348); ++ ++ ch348->udev = serial->dev; ++ ch348->serial = serial; ++ mutex_init(&ch348->write_lock); ++ ++ for (i = 0; i < CH348_MAXPORT; i++) ++ init_completion(&ch348->ports[i].write_completion); ++ ++ ch348->status_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!ch348->status_urb) { ++ ret = -ENOMEM; ++ goto err_free_ch348; ++ } ++ ++ usb_fill_bulk_urb(ch348->status_urb, ch348->udev, ++ usb_rcvbulkpipe(ch348->udev, epstatus->bEndpointAddress), ++ ch348->status_buffer, status_buffer_size, ++ ch348_process_status_urb, ch348); ++ ++ ret = usb_submit_urb(ch348->status_urb, GFP_KERNEL); ++ if (ret) { ++ dev_err(&ch348->udev->dev, ++ "%s - failed to submit status/interrupt urb %i\n", ++ __func__, ret); ++ goto err_free_status_urb; ++ } ++ ++ ret = usb_serial_generic_submit_read_urbs(port0, GFP_KERNEL); ++ if (ret) ++ goto err_kill_status_urb; ++ ++ ch348->cmd_ep = usb_sndbulkpipe(usb_dev, epcmd->bEndpointAddress); ++ ++ return 0; ++ ++err_kill_status_urb: ++ usb_kill_urb(ch348->status_urb); ++err_free_status_urb: ++ usb_free_urb(ch348->status_urb); ++err_free_ch348: ++ kfree(ch348); ++ return ret; ++} ++ ++static void ch348_release(struct usb_serial *serial) ++{ ++ struct ch348 *ch348 = usb_get_serial_data(serial); ++ ++ usb_kill_urb(ch348->status_urb); ++ usb_free_urb(ch348->status_urb); ++ ++ kfree(ch348); ++} ++ ++static void ch348_print_version(struct usb_serial *serial) ++{ ++ u8 *version_buf; ++ int ret; ++ ++ version_buf = kzalloc(4, GFP_KERNEL); ++ if (!version_buf) ++ return; ++ ++ ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), ++ CMD_VER, ++ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, ++ 0, 0, version_buf, 4, CH348_CMD_TIMEOUT); ++ if (ret < 0) ++ dev_dbg(&serial->dev->dev, "Failed to read CMD_VER: %d\n", ret); ++ else ++ dev_info(&serial->dev->dev, "Found WCH CH348%s\n", ++ (version_buf[1] & 0x80) ? "Q" : "L"); ++ ++ kfree(version_buf); ++} ++ ++static int ch348_probe(struct usb_serial *serial, const struct usb_device_id *id) ++{ ++ struct usb_endpoint_descriptor *epread, *epwrite, *epstatus, *epcmd; ++ struct usb_device *usb_dev = serial->dev; ++ struct usb_interface *intf; ++ int ret; ++ ++ intf = usb_ifnum_to_if(usb_dev, 0); ++ ++ ret = usb_find_common_endpoints(intf->cur_altsetting, &epread, &epwrite, ++ NULL, NULL); ++ if (ret) { ++ dev_err(&serial->dev->dev, "Failed to find basic endpoints ret=%d\n", ret); ++ return ret; ++ } ++ ++ epstatus = &intf->cur_altsetting->endpoint[2].desc; ++ if (!usb_endpoint_is_bulk_in(epstatus)) { ++ dev_err(&serial->dev->dev, "Missing second bulk in (STATUS/INT)\n"); ++ return -ENODEV; ++ } ++ ++ epcmd = &intf->cur_altsetting->endpoint[3].desc; ++ if (!usb_endpoint_is_bulk_out(epcmd)) { ++ dev_err(&serial->dev->dev, "Missing second bulk out (CMD)\n"); ++ return -ENODEV; ++ } ++ ++ ch348_print_version(serial); ++ ++ return 0; ++} ++ ++static int ch348_calc_num_ports(struct usb_serial *serial, ++ struct usb_serial_endpoints *epds) ++{ ++ int i; ++ ++ for (i = 1; i < CH348_MAXPORT; ++i) { ++ epds->bulk_out[i] = epds->bulk_out[0]; ++ epds->bulk_in[i] = epds->bulk_in[0]; ++ } ++ ++ epds->num_bulk_out = CH348_MAXPORT; ++ epds->num_bulk_in = CH348_MAXPORT; ++ ++ return CH348_MAXPORT; ++} ++ ++static int ch348_suspend(struct usb_serial *serial, pm_message_t message) ++{ ++ struct ch348 *ch348 = usb_get_serial_data(serial); ++ ++ usb_kill_urb(ch348->status_urb); ++ ++ return 0; ++} ++ ++static int ch348_resume(struct usb_serial *serial) ++{ ++ struct ch348 *ch348 = usb_get_serial_data(serial); ++ int ret; ++ ++ ret = usb_submit_urb(ch348->status_urb, GFP_KERNEL); ++ if (ret) { ++ dev_err(&ch348->udev->dev, ++ "%s - failed to submit status/interrupt urb %i\n", ++ __func__, ret); ++ return ret; ++ } ++ ++ ret = usb_serial_generic_resume(serial); ++ if (ret) ++ usb_kill_urb(ch348->status_urb); ++ ++ return ret; ++} ++ ++static const struct usb_device_id ch348_ids[] = { ++ { USB_DEVICE(0x1a86, 0x55d9), }, ++ { /* sentinel */ } ++}; ++ ++MODULE_DEVICE_TABLE(usb, ch348_ids); ++ ++static struct usb_serial_driver ch348_device = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ch348", ++ }, ++ .id_table = ch348_ids, ++ .num_ports = CH348_MAXPORT, ++ .num_bulk_in = 1, ++ .num_bulk_out = 1, ++ .open = ch348_open, ++ .set_termios = ch348_set_termios, ++ .process_read_urb = ch348_process_read_urb, ++ .prepare_write_buffer = ch348_prepare_write_buffer, ++ .write = ch348_write, ++ .probe = ch348_probe, ++ .calc_num_ports = ch348_calc_num_ports, ++ .attach = ch348_attach, ++ .release = ch348_release, ++ .suspend = ch348_suspend, ++ .resume = ch348_resume, ++}; ++ ++static struct usb_serial_driver * const serial_drivers[] = { ++ &ch348_device, NULL ++}; ++ ++module_usb_serial_driver(serial_drivers, ch348_ids); ++ ++MODULE_AUTHOR("Corentin Labbe "); ++MODULE_DESCRIPTION("USB CH348 Octo port serial converter driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/pending-6.12/900-net-ag71xx-fix-qca9530-and-qca9550-mdio-probe.patch b/target/linux/generic/pending-6.12/900-net-ag71xx-fix-qca9530-and-qca9550-mdio-probe.patch new file mode 100644 index 00000000000..c0e318e86e8 --- /dev/null +++ b/target/linux/generic/pending-6.12/900-net-ag71xx-fix-qca9530-and-qca9550-mdio-probe.patch @@ -0,0 +1,25 @@ +From 440415703692af4548e836832ef0434e87fbc357 Mon Sep 17 00:00:00 2001 +From: Oskari Lemmela +Date: Sat, 13 Jul 2024 18:56:59 +0300 +Subject: [PATCH] net: ag71xx: fix qca9530 and qca9550 mdio probe + +Newer QCA9530 and QCA9550 devices should use same div table as AR933X. + +Fixes: d51b6ce441d3 ("net: ethernet: add ag71xx driver") +Signed-off-by: Oskari Lemmela +--- + drivers/net/ethernet/atheros/ag71xx.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -641,7 +641,8 @@ static int ag71xx_mdio_get_divider(struc + if (!ref_clock) + return -EINVAL; + +- if (ag71xx_is(ag, AR9330) || ag71xx_is(ag, AR9340)) { ++ if (ag71xx_is(ag, AR9330) || ag71xx_is(ag, AR9340) || ++ ag71xx_is(ag, QCA9530) || ag71xx_is(ag, QCA9550)) { + table = ar933x_mdio_div_table; + ndivs = ARRAY_SIZE(ar933x_mdio_div_table); + } else if (ag71xx_is(ag, AR7240)) { diff --git a/target/linux/generic/pending-6.12/910-crypto-testmgr-Add-test-vectors-for-authenc-hmac-md5.patch b/target/linux/generic/pending-6.12/910-crypto-testmgr-Add-test-vectors-for-authenc-hmac-md5.patch new file mode 100644 index 00000000000..2d33d2ad59d --- /dev/null +++ b/target/linux/generic/pending-6.12/910-crypto-testmgr-Add-test-vectors-for-authenc-hmac-md5.patch @@ -0,0 +1,297 @@ +From dd227f442774a570ffccdfbc9536fec3b4666305 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Sat, 31 Jan 2026 12:35:30 +0100 +Subject: [PATCH] crypto: testmgr - Add test vectors for + authenc(hmac(md5),cbc(aes)) + +Test vectors were generated starting from existing CBC(AES) test vectors +(RFC3602, NIST SP800-38A) and adding HMAC(MD5) computed with Python +script. Then, the results were double-checked on Mediatek MT7981 (safexcel) +and NXP P2020 (talitos). Both platforms pass self-tests. + +Signed-off-by: Aleksander Jan Bajkowski +--- + crypto/testmgr.c | 7 ++ + crypto/testmgr.h | 255 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 262 insertions(+) + +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -4375,6 +4375,13 @@ static const struct alg_test_desc alg_te + .cprng = __VECS(ansi_cprng_aes_tv_template) + } + }, { ++ .alg = "authenc(hmac(md5),cbc(aes))", ++ .generic_driver = "authenc(hmac-md5-lib,cbc(aes-generic))", ++ .test = alg_test_aead, ++ .suite = { ++ .aead = __VECS(hmac_md5_aes_cbc_tv_temp) ++ } ++ }, { + .alg = "authenc(hmac(md5),cbc(des))", + .generic_driver = "authenc(hmac-md5-lib,cbc(des-generic))", + .test = alg_test_aead, +--- a/crypto/testmgr.h ++++ b/crypto/testmgr.h +@@ -17133,6 +17133,261 @@ static const struct cipher_testvec aes_c + }, + }; + ++static const struct aead_testvec hmac_md5_aes_cbc_tv_temp[] = { ++ { /* RFC 3602 Case 1 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" ++ "\x51\x2e\x03\xd5\x34\x12\x00\x06", ++ .klen = 8 + 16 + 16, ++ .iv = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" ++ "\xb4\x22\xda\x80\x2c\x9f\xac\x41", ++ .assoc = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30" ++ "\xb4\x22\xda\x80\x2c\x9f\xac\x41", ++ .alen = 16, ++ .ptext = "Single block msg", ++ .plen = 16, ++ .ctext = "\xe3\x53\x77\x9c\x10\x79\xae\xb8" ++ "\x27\x08\x94\x2d\xbe\x77\x18\x1a" ++ "\x22\x10\xf2\x25\x7f\xe9\x0d\x92" ++ "\xfc\x00\x55\xb1\xd0\xb5\x3a\x74", ++ .clen = 16 + 16, ++ }, { /* RFC 3602 Case 2 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\xc2\x86\x69\x6d\x88\x7c\x9a\xa0" ++ "\x61\x1b\xbb\x3e\x20\x25\xa4\x5a", ++ .klen = 8 + 16 + 16, ++ .iv = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" ++ "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", ++ .assoc = "\x56\x2e\x17\x99\x6d\x09\x3d\x28" ++ "\xdd\xb3\xba\x69\x5a\x2e\x6f\x58", ++ .alen = 16, ++ .ptext = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ++ .plen = 32, ++ .ctext = "\xd2\x96\xcd\x94\xc2\xcc\xcf\x8a" ++ "\x3a\x86\x30\x28\xb5\xe1\xdc\x0a" ++ "\x75\x86\x60\x2d\x25\x3c\xff\xf9" ++ "\x1b\x82\x66\xbe\xa6\xd6\x1a\xb1" ++ "\x31\xef\xd1\x5e\x2d\x83\xde\x59" ++ "\x5c\x63\x6c\xd6\x6e\x96\x8c\x5b", ++ .clen = 32 + 16, ++ }, { /* RFC 3602 Case 3 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x6c\x3e\xa0\x47\x76\x30\xce\x21" ++ "\xa2\xce\x33\x4a\xa7\x46\xc2\xcd", ++ .klen = 8 + 16 + 16, ++ .iv = "\xc7\x82\xdc\x4c\x09\x8c\x66\xcb" ++ "\xd9\xcd\x27\xd8\x25\x68\x2c\x81", ++ .assoc = "\xc7\x82\xdc\x4c\x09\x8c\x66\xcb" ++ "\xd9\xcd\x27\xd8\x25\x68\x2c\x81", ++ .alen = 16, ++ .ptext = "This is a 48-byte message (exactly 3 AES blocks)", ++ .plen = 48, ++ .ctext = "\xd0\xa0\x2b\x38\x36\x45\x17\x53" ++ "\xd4\x93\x66\x5d\x33\xf0\xe8\x86" ++ "\x2d\xea\x54\xcd\xb2\x93\xab\xc7" ++ "\x50\x69\x39\x27\x67\x72\xf8\xd5" ++ "\x02\x1c\x19\x21\x6b\xad\x52\x5c" ++ "\x85\x79\x69\x5d\x83\xba\x26\x84" ++ "\xa1\x9e\xc5\x65\x43\xc5\x51\x70" ++ "\xb5\xc8\x38\xce\xbb\x3b\xc6\x0f", ++ .clen = 48 + 16, ++ }, { /* RFC 3602 Case 4 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x56\xe4\x7a\x38\xc5\x59\x89\x74" ++ "\xbc\x46\x90\x3d\xba\x29\x03\x49", ++ .klen = 8 + 16 + 16, ++ .iv = "\x8c\xe8\x2e\xef\xbe\xa0\xda\x3c" ++ "\x44\x69\x9e\xd7\xdb\x51\xb7\xd9", ++ .assoc = "\x8c\xe8\x2e\xef\xbe\xa0\xda\x3c" ++ "\x44\x69\x9e\xd7\xdb\x51\xb7\xd9", ++ .alen = 16, ++ .ptext = "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" ++ "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" ++ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" ++ "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" ++ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" ++ "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" ++ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" ++ "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf", ++ .plen = 64, ++ .ctext = "\xc3\x0e\x32\xff\xed\xc0\x77\x4e" ++ "\x6a\xff\x6a\xf0\x86\x9f\x71\xaa" ++ "\x0f\x3a\xf0\x7a\x9a\x31\xa9\xc6" ++ "\x84\xdb\x20\x7e\xb0\xef\x8e\x4e" ++ "\x35\x90\x7a\xa6\x32\xc3\xff\xdf" ++ "\x86\x8b\xb7\xb2\x9d\x3d\x46\xad" ++ "\x83\xce\x9f\x9a\x10\x2e\xe9\x9d" ++ "\x49\xa5\x3e\x87\xf4\xc3\xda\x55" ++ "\x19\x90\xcc\x2c\x6d\x76\x0f\xd6" ++ "\x6c\x54\x09\xb1\x3e\x98\x0c\x11", ++ .clen = 64 + 16, ++ }, { /* RFC 3602 Case 5 */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x10" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x90\xd3\x82\xb4\x10\xee\xba\x7a" ++ "\xd9\x38\xc4\x6c\xec\x1a\x82\xbf", ++ .klen = 8 + 16 + 16, ++ .iv = "\xe9\x6e\x8c\x08\xab\x46\x57\x63" ++ "\xfd\x09\x8d\x45\xdd\x3f\xf8\x93", ++ .assoc = "\x00\x00\x43\x21\x00\x00\x00\x01" ++ "\xe9\x6e\x8c\x08\xab\x46\x57\x63" ++ "\xfd\x09\x8d\x45\xdd\x3f\xf8\x93", ++ .alen = 24, ++ .ptext = "\x08\x00\x0e\xbd\xa7\x0a\x00\x00" ++ "\x8e\x9c\x08\x3d\xb9\x5b\x07\x00" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ++ "\x10\x11\x12\x13\x14\x15\x16\x17" ++ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" ++ "\x20\x21\x22\x23\x24\x25\x26\x27" ++ "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ++ "\x30\x31\x32\x33\x34\x35\x36\x37" ++ "\x01\x02\x03\x04\x05\x06\x07\x08" ++ "\x09\x0a\x0b\x0c\x0d\x0e\x0e\x01", ++ .plen = 80, ++ .ctext = "\xf6\x63\xc2\x5d\x32\x5c\x18\xc6" ++ "\xa9\x45\x3e\x19\x4e\x12\x08\x49" ++ "\xa4\x87\x0b\x66\xcc\x6b\x99\x65" ++ "\x33\x00\x13\xb4\x89\x8d\xc8\x56" ++ "\xa4\x69\x9e\x52\x3a\x55\xdb\x08" ++ "\x0b\x59\xec\x3a\x8e\x4b\x7e\x52" ++ "\x77\x5b\x07\xd1\xdb\x34\xed\x9c" ++ "\x53\x8a\xb5\x0c\x55\x1b\x87\x4a" ++ "\xa2\x69\xad\xd0\x47\xad\x2d\x59" ++ "\x13\xac\x19\xb7\xcf\xba\xd4\xa6" ++ "\x9f\x6f\xa4\x85\x28\xf1\xc9\xea" ++ "\xe1\xd0\x7d\x30\x4a\xd0\x81\x12", ++ .clen = 80 + 16, ++ }, { /* NIST SP800-38A F.2.3 CBC-AES192.Encrypt */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x18" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x8e\x73\xb0\xf7\xda\x0e\x64\x52" ++ "\xc8\x10\xf3\x2b\x80\x90\x79\xe5" ++ "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", ++ .klen = 8 + 16 + 24, ++ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .alen = 16, ++ .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" ++ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" ++ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" ++ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" ++ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" ++ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" ++ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", ++ .plen = 64, ++ .ctext = "\x4f\x02\x1d\xb2\x43\xbc\x63\x3d" ++ "\x71\x78\x18\x3a\x9f\xa0\x71\xe8" ++ "\xb4\xd9\xad\xa9\xad\x7d\xed\xf4" ++ "\xe5\xe7\x38\x76\x3f\x69\x14\x5a" ++ "\x57\x1b\x24\x20\x12\xfb\x7a\xe0" ++ "\x7f\xa9\xba\xac\x3d\xf1\x02\xe0" ++ "\x08\xb0\xe2\x79\x88\x59\x88\x81" ++ "\xd9\x20\xa9\xe6\x4f\x56\x15\xcd" ++ "\xc3\x46\xe5\x2c\x07\x27\x50\xca" ++ "\x50\x4a\x83\x5f\x72\xd9\x76\x8d", ++ .clen = 64 + 16, ++ }, { /* NIST SP800-38A F.2.5 CBC-AES256.Encrypt */ ++#ifdef __LITTLE_ENDIAN ++ .key = "\x08\x00" /* rta length */ ++ "\x01\x00" /* rta type */ ++#else ++ .key = "\x00\x08" /* rta length */ ++ "\x00\x01" /* rta type */ ++#endif ++ "\x00\x00\x00\x20" /* enc key length */ ++ "\x11\x22\x33\x44\x55\x66\x77\x88" ++ "\x99\xaa\xbb\xcc\xdd\xee\xff\x11" ++ "\x60\x3d\xeb\x10\x15\xca\x71\xbe" ++ "\x2b\x73\xae\xf0\x85\x7d\x77\x81" ++ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7" ++ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4", ++ .klen = 8 + 16 + 32, ++ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07" ++ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ++ .alen = 16, ++ .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" ++ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c" ++ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" ++ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11" ++ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" ++ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17" ++ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10", ++ .plen = 64, ++ .ctext = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba" ++ "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6" ++ "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d" ++ "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d" ++ "\x39\xf2\x33\x69\xa9\xd9\xba\xcf" ++ "\xa5\x30\xe2\x63\x04\x23\x14\x61" ++ "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc" ++ "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b" ++ "\x59\x62\x06\x71\x57\xdf\x18\x15" ++ "\x32\x02\xfa\xce\x2c\xd2\x1a\x8d", ++ .clen = 64 + 16, ++ }, ++}; ++ + static const struct aead_testvec hmac_md5_ecb_cipher_null_tv_template[] = { + { /* Input data from RFC 2410 Case 1 */ + #ifdef __LITTLE_ENDIAN diff --git a/target/linux/generic/pending-6.12/920-mangle_bootargs.patch b/target/linux/generic/pending-6.12/920-mangle_bootargs.patch new file mode 100644 index 00000000000..3bf2fce4fbe --- /dev/null +++ b/target/linux/generic/pending-6.12/920-mangle_bootargs.patch @@ -0,0 +1,71 @@ +From: Imre Kaloz +Subject: init: add CONFIG_MANGLE_BOOTARGS and disable it by default + +Enabling this option renames the bootloader supplied root= +and rootfstype= variables, which might have to be know but +would break the automatisms OpenWrt uses. + +Signed-off-by: Imre Kaloz +--- + init/Kconfig | 9 +++++++++ + init/main.c | 24 ++++++++++++++++++++++++ + 2 files changed, 33 insertions(+) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1888,6 +1888,15 @@ config ARCH_HAS_MEMBARRIER_CALLBACKS + config ARCH_HAS_MEMBARRIER_SYNC_CORE + bool + ++config MANGLE_BOOTARGS ++ bool "Rename offending bootargs" ++ depends on EXPERT ++ help ++ Sometimes the bootloader passed bogus root= and rootfstype= ++ parameters to the kernel, and while you want to ignore them, ++ you need to know the values f.e. to support dual firmware ++ layouts on the flash. ++ + config HAVE_PERF_EVENTS + bool + help +--- a/init/main.c ++++ b/init/main.c +@@ -633,6 +633,29 @@ static inline void setup_nr_cpu_ids(void + static inline void smp_prepare_cpus(unsigned int maxcpus) { } + #endif + ++#ifdef CONFIG_MANGLE_BOOTARGS ++static void __init mangle_bootargs(char *command_line) ++{ ++ char *rootdev; ++ char *rootfs; ++ ++ rootdev = strstr(command_line, "root=/dev/mtdblock"); ++ ++ if (rootdev) ++ strncpy(rootdev, "mangled_rootblock=", 18); ++ ++ rootfs = strstr(command_line, "rootfstype"); ++ ++ if (rootfs) ++ strncpy(rootfs, "mangled_fs", 10); ++ ++} ++#else ++static void __init mangle_bootargs(char *command_line) ++{ ++} ++#endif ++ + /* + * We need to store the untouched command line for future reference. + * We also need to store the touched command line since the parameter +@@ -939,6 +962,7 @@ void start_kernel(void) + jump_label_init(); + static_call_init(); + early_security_init(); ++ mangle_bootargs(command_line); + setup_boot_config(); + setup_command_line(command_line); + setup_nr_cpu_ids();