+++ /dev/null
-From 71e5da46e78c1cd24e2feed251a2845327447ad8 Mon Sep 17 00:00:00 2001
-From: Kees Cook <kees@kernel.org>
-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 <kees@kernel.org>
-Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-Link: https://patch.msgid.link/20250521212707.1767879-3-Jason@zx2c4.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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);
+++ /dev/null
-From b35108a51cf7bab58d7eace1267d7965978bcdb8 Mon Sep 17 00:00:00 2001
-From: Easwar Hariharan <eahariha@linux.microsoft.com>
-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 <mhklinux@outlook.com>
-Signed-off-by: Easwar Hariharan <eahariha@linux.microsoft.com>
-Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-Reviewed-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
-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,
+++ /dev/null
-From bb2784d9ab49587ba4fbff37a319fff2924db289 Mon Sep 17 00:00:00 2001
-From: Easwar Hariharan <eahariha@linux.microsoft.com>
-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 <lkp@intel.com>
-Signed-off-by: Easwar Hariharan <eahariha@linux.microsoft.com>
-Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-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)
+++ /dev/null
-From 02ac5f9d6caec96071103f7c62b5117526e47b64 Mon Sep 17 00:00:00 2001
-From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
-Date: Thu, 24 Oct 2024 02:20:02 +0000
-Subject: [PATCH] of: property: add of_graph_get_next_port()
-
-We have endpoint base functions
- - of_graph_get_next_endpoint()
- - of_graph_get_endpoint_count()
- - for_each_endpoint_of_node()
-
-Here, for_each_endpoint_of_node() loop finds each endpoints
-
- ports {
- port@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 <kuninori.morimoto.gx@renesas.com>
-Link: https://lore.kernel.org/r/87ldyeb5t9.wl-kuninori.morimoto.gx@renesas.com
-Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
----
- drivers/of/property.c | 54 ++++++++++++++++++++++++++++++++++++++++
- include/linux/of_graph.h | 28 +++++++++++++++++++++
- 2 files changed, 82 insertions(+)
-
---- a/drivers/of/property.c
-+++ b/drivers/of/property.c
-@@ -631,6 +631,43 @@ struct device_node *of_graph_get_port_by
- EXPORT_SYMBOL(of_graph_get_port_by_id);
-
- /**
-+ * of_graph_get_next_port() - get next port node.
-+ * @parent: pointer to the parent device node, or parent ports node
-+ * @prev: previous port node, or NULL to get first
-+ *
-+ * Parent device node can be used as @parent whether device node has ports node
-+ * or not. It will work same as ports@0 node.
-+ *
-+ * Return: A 'port' node pointer with refcount incremented. Refcount
-+ * of the passed @prev node is decremented.
-+ */
-+struct device_node *of_graph_get_next_port(const struct device_node *parent,
-+ struct device_node *prev)
-+{
-+ if (!parent)
-+ return NULL;
-+
-+ if (!prev) {
-+ struct device_node *node __free(device_node) =
-+ of_get_child_by_name(parent, "ports");
-+
-+ if (node)
-+ parent = node;
-+
-+ return of_get_child_by_name(parent, "port");
-+ }
-+
-+ do {
-+ prev = of_get_next_child(parent, prev);
-+ if (!prev)
-+ break;
-+ } while (!of_node_name_eq(prev, "port"));
-+
-+ return prev;
-+}
-+EXPORT_SYMBOL(of_graph_get_next_port);
-+
-+/**
- * of_graph_get_next_endpoint() - get next endpoint node
- * @parent: pointer to the parent device node
- * @prev: previous endpoint node, or NULL to get first
-@@ -824,6 +861,23 @@ unsigned int of_graph_get_endpoint_count
- EXPORT_SYMBOL(of_graph_get_endpoint_count);
-
- /**
-+ * of_graph_get_port_count() - get the number of port in a device or ports node
-+ * @np: pointer to the device or ports node
-+ *
-+ * Return: count of port of this device or ports node
-+ */
-+unsigned int of_graph_get_port_count(struct device_node *np)
-+{
-+ unsigned int num = 0;
-+
-+ for_each_of_graph_port(np, port)
-+ num++;
-+
-+ return num;
-+}
-+EXPORT_SYMBOL(of_graph_get_port_count);
-+
-+/**
- * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
- * @node: pointer to parent device_node containing graph port/endpoint
- * @port: identifier (value of reg property) of the parent port node
---- a/include/linux/of_graph.h
-+++ b/include/linux/of_graph.h
-@@ -11,6 +11,7 @@
- #ifndef __LINUX_OF_GRAPH_H
- #define __LINUX_OF_GRAPH_H
-
-+#include <linux/cleanup.h>
- #include <linux/types.h>
- #include <linux/errno.h>
-
-@@ -37,14 +38,29 @@ struct of_endpoint {
- for (child = of_graph_get_next_endpoint(parent, NULL); child != NULL; \
- child = of_graph_get_next_endpoint(parent, child))
-
-+/**
-+ * for_each_of_graph_port - iterate over every port in a device or ports node
-+ * @parent: parent device or ports node containing port
-+ * @child: loop variable pointing to the current port node
-+ *
-+ * When breaking out of the loop, and continue to use the @child, you need to
-+ * use return_ptr(@child) or no_free_ptr(@child) not to call __free() for it.
-+ */
-+#define for_each_of_graph_port(parent, child) \
-+ for (struct device_node *child __free(device_node) = of_graph_get_next_port(parent, NULL);\
-+ child != NULL; child = of_graph_get_next_port(parent, child))
-+
- #ifdef CONFIG_OF
- bool of_graph_is_present(const struct device_node *node);
- int of_graph_parse_endpoint(const struct device_node *node,
- struct of_endpoint *endpoint);
- unsigned int of_graph_get_endpoint_count(const struct device_node *np);
-+unsigned int of_graph_get_port_count(struct device_node *np);
- struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
- struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
- struct device_node *previous);
-+struct device_node *of_graph_get_next_port(const struct device_node *parent,
-+ struct device_node *port);
- struct device_node *of_graph_get_endpoint_by_regs(
- const struct device_node *parent, int port_reg, int reg);
- struct device_node *of_graph_get_remote_endpoint(
-@@ -73,6 +89,11 @@ static inline unsigned int of_graph_get_
- return 0;
- }
-
-+static inline unsigned int of_graph_get_port_count(struct device_node *np)
-+{
-+ return 0;
-+}
-+
- static inline struct device_node *of_graph_get_port_by_id(
- struct device_node *node, u32 id)
- {
-@@ -83,6 +104,13 @@ static inline struct device_node *of_gra
- const struct device_node *parent,
- struct device_node *previous)
- {
-+ return NULL;
-+}
-+
-+static inline struct device_node *of_graph_get_next_port(
-+ const struct device_node *parent,
-+ struct device_node *previous)
-+{
- return NULL;
- }
-
+++ /dev/null
-From 51e32e897539663957f7a0950f66b48f8896efee Mon Sep 17 00:00:00 2001
-From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Date: Sat, 19 Oct 2024 14:16:00 +0300
-Subject: [PATCH] clk: Provide devm_clk_bulk_get_all_enabled() helper
-
-Commit 265b07df758a ("clk: Provide managed helper to get and enable bulk
-clocks") added devm_clk_bulk_get_all_enable() function, but missed to
-return the number of clocks stored in the clk_bulk_data table referenced
-by the clks argument. Without knowing the number, it's not possible to
-iterate these clocks when needed, hence the argument is useless and
-could have been simply removed.
-
-Introduce devm_clk_bulk_get_all_enabled() variant, which is consistent
-with devm_clk_bulk_get_all() in terms of the returned value:
-
- > 0 if one or more clocks have been stored
- = 0 if there are no clocks
- < 0 if an error occurred
-
-Moreover, the naming is consistent with devm_clk_get_enabled(), i.e. use
-the past form of 'enable'.
-
-To reduce code duplication and improve patch readability, make
-devm_clk_bulk_get_all_enable() use the new helper, as suggested by
-Stephen Boyd.
-
-Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
-Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Link: https://lore.kernel.org/r/20241019-clk_bulk_ena_fix-v4-1-57f108f64e70@collabora.com
-Signed-off-by: Stephen Boyd <sboyd@kernel.org>
----
- drivers/clk/clk-devres.c | 9 +++++----
- include/linux/clk.h | 21 ++++++++++++++++-----
- 2 files changed, 21 insertions(+), 9 deletions(-)
-
---- a/drivers/clk/clk-devres.c
-+++ b/drivers/clk/clk-devres.c
-@@ -218,8 +218,8 @@ static void devm_clk_bulk_release_all_en
- clk_bulk_put_all(devres->num_clks, devres->clks);
- }
-
--int __must_check devm_clk_bulk_get_all_enable(struct device *dev,
-- struct clk_bulk_data **clks)
-+int __must_check devm_clk_bulk_get_all_enabled(struct device *dev,
-+ struct clk_bulk_data **clks)
- {
- struct clk_bulk_devres *devres;
- int ret;
-@@ -244,11 +244,12 @@ int __must_check devm_clk_bulk_get_all_e
- } else {
- clk_bulk_put_all(devres->num_clks, devres->clks);
- devres_free(devres);
-+ return ret;
- }
-
-- return ret;
-+ return devres->num_clks;
- }
--EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all_enable);
-+EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all_enabled);
-
- static int devm_clk_match(struct device *dev, void *res, void *data)
- {
---- a/include/linux/clk.h
-+++ b/include/linux/clk.h
-@@ -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)
- {
+++ /dev/null
-From 64ee3cf096ac590e7da2ceac1c390546bff5e240 Mon Sep 17 00:00:00 2001
-From: "Rob Herring (Arm)" <robh@kernel.org>
-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 <m.szyprowski@samsung.com>
-Reported-by: Steven Price <steven.price@arm.com>
-Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
-Link: https://lore.kernel.org/r/20241108193547.2647986-2-robh@kernel.org
-Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
----
- 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]);
+++ /dev/null
-From 6e5773d52f4a2d9c80692245f295069260cff6fc Mon Sep 17 00:00:00 2001
-From: "Rob Herring (Arm)" <robh@kernel.org>
-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 <sean.anderson@linux.dev>
-Link: https://lore.kernel.org/r/20250110215030.3637845-2-robh@kernel.org
-Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
----
- 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 */
+++ /dev/null
-From f4fcfdda2fd8834c62dcb9bfddcf1f89d190b70e Mon Sep 17 00:00:00 2001
-From: "Rob Herring (Arm)" <robh@kernel.org>
-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 <daniel.baluta@nxp.com>
-Acked-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
-Link: https://lore.kernel.org/r/20250423-dt-memory-region-v2-v2-1-2fbd6ebd3c88@kernel.org
-Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
----
- 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 <linux/err.h>
-+#include <linux/ioport.h>
- #include <linux/libfdt.h>
- #include <linux/of.h>
- #include <linux/of_fdt.h>
-@@ -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
-
- /**
+++ /dev/null
-From 239d87327dcd361b0098038995f8908f3296864f Mon Sep 17 00:00:00 2001
-From: Kees Cook <kees@kernel.org>
-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" <linux@weissschuh.net>
-Closes: https://lore.kernel.org/all/db7190c8-d17f-4a0d-bc2f-5903c79f36c2@t-8ch.de/
-Reported-by: Nilay Shroff <nilay@linux.ibm.com>
-Closes: https://lore.kernel.org/all/20241112124127.1666300-1-nilay@linux.ibm.com/
-Tested-by: Nilay Shroff <nilay@linux.ibm.com>
-Acked-by: Yury Norov <yury.norov@gmail.com>
-Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Signed-off-by: Kees Cook <kees@kernel.org>
----
- 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); \
- })
-
- /*
+++ /dev/null
-From aea70964b5a7ca491a3701f2dde6c9d05d51878d Mon Sep 17 00:00:00 2001
-From: "Rob Herring (Arm)" <robh@kernel.org>
-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 <iuliana.prodan@nxp.com>
-Reported-by: Daniel Baluta <daniel.baluta@gmail.com>
-Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
-Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
-Reviewed-by: Saravana Kannan <saravanak@google.com>
-Link: https://lore.kernel.org/r/20250820192805.565568-1-robh@kernel.org
-Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
----
- 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;
- }
+++ /dev/null
-From ae461cde5c559675fc4c0ba351c7c31ace705f56 Mon Sep 17 00:00:00 2001
-From: Bohdan Chubuk <chbgdn@gmail.com>
-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 <chbgdn@gmail.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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 = {
+++ /dev/null
-From 5f284dc15ca8695d0394414045ac64616a3b0e69 Mon Sep 17 00:00:00 2001
-From: Tianling Shen <cnsztl@gmail.com>
-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 <cnsztl@gmail.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-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 <jon.lin@rock-chips.com>
-+ */
-+
-+#include <linux/device.h>
-+#include <linux/kernel.h>
-+#include <linux/mtd/spinand.h>
-+
-+#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;
+++ /dev/null
-From 6b88293aae7fb78872e5cc1ec36e2f750ae12e38 Mon Sep 17 00:00:00 2001
-From: Markus Stockhausen <markus.stockhausen@gmx.de>
-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 <miquel.raynal@bootlin.com>
-Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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 <linux/mtd/nand.h>
-
- /**
-+ * 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);
-
+++ /dev/null
-From a1d3bc606bf5c3b3ea811cc2019df6285d75b00f Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-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 <mikhail.kshevetskiy@iopsys.eu>
-Tested-by: Tianling Shen <cnsztl@gmail.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-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)),
- };
-
+++ /dev/null
-From 8c52932da5e6756fa66f52f0720da283fba13aa6 Mon Sep 17 00:00:00 2001
-From: Md Sadre Alam <quic_mdalam@quicinc.com>
-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 <quic_mdalam@quicinc.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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,
- };
-
+++ /dev/null
-From 1d479f5b345e0c3650fec4dddeef9fc6fab30c8b Mon Sep 17 00:00:00 2001
-From: Md Sadre Alam <quic_mdalam@quicinc.com>
-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 <konrad.dybcio@oss.qualcomm.com>
-Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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");
+++ /dev/null
-From fdf3ee5c6e5278dab4f60b998b47ed2d510bf80f Mon Sep 17 00:00:00 2001
-From: Md Sadre Alam <quic_mdalam@quicinc.com>
-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 <quic_mdalam@quicinc.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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 <linux/clk.h>
-+#include <linux/delay.h>
-+#include <linux/dmaengine.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/dma/qcom_adm.h>
-+#include <linux/dma/qcom_bam_dma.h>
-+#include <linux/module.h>
-+#include <linux/of.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+#include <linux/mtd/nand-qpic-common.h>
-+
-+/**
-+ * 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 <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/slab.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;
--};
-+#include <linux/mtd/nand-qpic-common.h>
-
- /*
- * 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 <quic_mdalam@quicinc.com>
-+ *
-+ */
-+#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
-+
+++ /dev/null
-From 0c08080fd71cd5dd59643104b39d3c89d793ab3c Mon Sep 17 00:00:00 2001
-From: Md Sadre Alam <quic_mdalam@quicinc.com>
-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 <konrad.dybcio@oss.qualcomm.com>
-Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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
+++ /dev/null
-From 9d4ffbcfde283f2a87ea45128ddf7e6651facdd9 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <ansuelsmth@gmail.com>
----
- 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);
+++ /dev/null
-From b9371866799d67a80be0ea9e01bd41987db22f26 Mon Sep 17 00:00:00 2001
-From: Md Sadre Alam <quic_mdalam@quicinc.com>
-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 <quic_mdalam@quicinc.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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;
-+
-+ );
- };
-
- /*
+++ /dev/null
-From 7304d1909080ef0c9da703500a97f46c98393fcd Mon Sep 17 00:00:00 2001
-From: Md Sadre Alam <quic_mdalam@quicinc.com>
-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 <quic_srichara@quicinc.com>
-Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
-Co-developed-by: Varadarajan Narayanan <quic_varada@quicinc.com>
-Signed-off-by: Varadarajan Narayanan <quic_varada@quicinc.com>
-Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
-Link: https://patch.msgid.link/20250224111414.2809669-3-quic_mdalam@quicinc.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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 <quic_mdalam@quicinc.com>
-+ * Sricharan R <quic_srichara@quicinc.com>
-+ * Varadarajan Narayanan <quic_varada@quicinc.com>
-+ */
-+#include <linux/bitops.h>
-+#include <linux/clk.h>
-+#include <linux/delay.h>
-+#include <linux/dmaengine.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/dma/qcom_adm.h>
-+#include <linux/dma/qcom_bam_dma.h>
-+#include <linux/module.h>
-+#include <linux/of.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+#include <linux/mtd/nand-qpic-common.h>
-+#include <linux/mtd/spinand.h>
-+#include <linux/bitfield.h>
-+
-+#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 <quic_mdalam@quicinc.com>");
-+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 {
+++ /dev/null
-From cf1ba3cb245020459f2ca446b7a7b199839f5d83 Mon Sep 17 00:00:00 2001
-From: Dan Carpenter <dan.carpenter@linaro.org>
-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 <dan.carpenter@linaro.org>
-Link: https://patch.msgid.link/2f4b0a0b-2c03-41c0-8a4a-3d789a83832d@stanley.mountain
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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);
+++ /dev/null
-From d450cdd9c4398add1f2aa7200f2c95f1e3b9f9fa Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Link: https://patch.msgid.link/20250313-qpic-snand-memleak-fix-v1-1-e54e78d1da3a@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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);
-
+++ /dev/null
-From d32c4e58545f17caaa854415f854691e32d42075 Mon Sep 17 00:00:00 2001
-From: Geert Uytterhoeven <geert+renesas@glider.be>
-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 <geert+renesas@glider.be>
-Link: https://patch.msgid.link/b63db431cbf35223a4400e44c296293d32c4543c.1742998909.git.geert+renesas@glider.be
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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 = {
+++ /dev/null
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Reviewed-by: Abel Vesa <abel.vesa@linaro.org>
-Reviewed-by: Md Sadre Alam <quic_mdalam@quicinc.com>
----
- 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;
- }
+++ /dev/null
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Reviewed-by: Md Sadre Alam <quic_mdalam@quicinc.com>
----
- 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;
- }
+++ /dev/null
-From f48d80503504257682e493dc17408f2f0b47bcfa Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Link: https://patch.msgid.link/20250320-qpic-snand-kmalloc-v1-1-94e267550675@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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);
+++ /dev/null
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
----
- 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;
+++ /dev/null
-From 36c6468724aa98d33fea9a1d7e07ddda6302f5d4 Mon Sep 17 00:00:00 2001
-From: Geert Uytterhoeven <geert+renesas@glider.be>
-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 <lkp@intel.com>
-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 <geert+renesas@glider.be>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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/
+++ /dev/null
-From 65cb56d49f6edea409600a3c61effc70ee5d43d8 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Link: https://patch.msgid.link/20250501-qpic-snand-validate-ecc-v1-1-532776581a66@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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)
+++ /dev/null
-From 2abf107dcd797c60c86e9f17319cd1658862f6b2 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Reviewed-by: Md Sadre Alam <quic_mdalam@quicinc.com>
-Link: https://patch.msgid.link/20250515-qpic-snand-use-bitmasks-v1-1-11729aeae73b@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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;
+++ /dev/null
-From d85d0380292a7e618915069c3579ae23c7c80339 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <quic_mdalam@quicinc.com>
-Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
-Link: https://patch.msgid.link/20250618-qpic-snand-avoid-mem-corruption-v3-1-319c71296cda@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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) |
+++ /dev/null
-From f820034864dd463cdcd2bebe7940f2eca0eb4223 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Link: https://patch.msgid.link/20250723-qpic-snand-fix-steps-v1-1-d800695dde4c@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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;
+++ /dev/null
-From 0dc7e656ddd54c3267b7cc18c1ac8ec1297ed02f Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <quic_mdalam@quicinc.com>
-Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
-Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-1-ae2c17a30bb7@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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
+++ /dev/null
-From 913bf8d50cbd144c87e9660b591781179182ff59 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-2-ae2c17a30bb7@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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;
+++ /dev/null
-From 6bc829220b33da8522572cc50fdf5067c51d3bf3 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Link: https://patch.msgid.link/20250801-qpic-snand-oob-cwpp-fix-v1-1-f5a41b86af2e@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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;
+++ /dev/null
-From 13d0fe84a214658254a7412b2b46ec1507dc51f0 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
-Link: https://patch.msgid.link/20250805-qpic-snand-oob-ecc-fix-v2-1-e6f811c70d6f@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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,
+++ /dev/null
-From 56fce75470041b5b0d92ae10637416e1a4cceb1b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
-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 <noltari@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: William Zhang <william.zhang@broadcom.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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;
+++ /dev/null
-From 528b541b71cf03e263272b051b70696f92258e9d Mon Sep 17 00:00:00 2001
-From: David Regan <dregan@broadcom.com>
-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 <dregan@broadcom.com>
-Reviewed-by: William Zhang <william.zhang@broadcom.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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
+++ /dev/null
-From 3bfb22cecfe6b6f0d8ee56ef4b533cf68599c5d9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
-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 <noltari@gmail.com>
-Reviewed-by: David Regan <dregan@broadcom.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: William Zhang <william.zhang@broadcom.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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.
+++ /dev/null
-From 1991a458528588ff34e98b6365362560d208710f Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-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 <j4g8y7@gmail.com>
-Message-ID: <20250903-qpic-snand-unregister-ecceng-v1-1-ef5387b0abdc@gmail.com>
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- 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);
+++ /dev/null
-From e4a0cf9f1d90e6888e5373da3314f761024f6c97 Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-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 <mikhail.kshevetskiy@iopsys.eu>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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,
+++ /dev/null
-From 004f8ea0d9917398aabff7388b3bf62a84a4088b Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-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 <mikhail.kshevetskiy@iopsys.eu>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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);
-
+++ /dev/null
-From 010dc7f2dd6a0078ade3f88f627ed5fbf45ceb94 Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-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 <mikhail.kshevetskiy@iopsys.eu>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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 +=
+++ /dev/null
-From 6d9d6ab3a82af50e36e13e7bc8e2d1b970e39f79 Mon Sep 17 00:00:00 2001
-From: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
-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 <Takahiro.Kuwano@infineon.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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
+++ /dev/null
-From 1a50e3612de9187857f55ee14a573f7f8e7d4ebc Mon Sep 17 00:00:00 2001
-From: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
-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 <kr.kim@skyhighmemory.com>
-Signed-off-by: KR Kim <kr.kim@skyhighmemory.com>
-Signed-off-by: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- 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 <takahiro.kuwano@infineon.com>
-+ * Co-Author: KR Kim <kr.kim@skyhighmemory.com>
-+ */
-+
-+#include <linux/device.h>
-+#include <linux/kernel.h>
-+#include <linux/mtd/spinand.h>
-+
-+#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;
+++ /dev/null
-From 03cb793b26834ddca170ba87057c8f883772dd45 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <ansuelsmth@gmail.com>
-Reviewed-by: Christoph Hellwig <hch@lst.de>
-Link: https://lore.kernel.org/r/20241002221306.4403-2-ansuelsmth@gmail.com
-Signed-off-by: Jens Axboe <axboe@kernel.dk>
----
- 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)
+++ /dev/null
-From e5f587242b6072ffab4f4a084a459a59f3035873 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <ansuelsmth@gmail.com>
-Reviewed-by: Christoph Hellwig <hch@lst.de>
-Link: https://lore.kernel.org/r/20241002221306.4403-4-ansuelsmth@gmail.com
-Signed-off-by: Jens Axboe <axboe@kernel.dk>
----
- 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)
+++ /dev/null
-From 45ff6c340ddfc2dade74d5b7a8962c778ab7042c Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <ansuelsmth@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Link: https://lore.kernel.org/r/20241002221306.4403-5-ansuelsmth@gmail.com
-Signed-off-by: Jens Axboe <axboe@kernel.dk>
----
- 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;
+++ /dev/null
-From 884555b557e5e6d41c866e2cd8d7b32f50ec974b Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <ansuelsmth@gmail.com>
-Reviewed-by: Christoph Hellwig <hch@lst.de>
-Link: https://lore.kernel.org/r/20241002221306.4403-6-ansuelsmth@gmail.com
-Signed-off-by: Jens Axboe <axboe@kernel.dk>
----
- 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 <linux/blkdev.h>
-+#include <linux/major.h>
-+#include <linux/of.h>
-+#include <linux/string.h>
-+#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;
-+}
+++ /dev/null
-From 8cc5f4cb94c0b1c7c1ba8013c14fd02ffb1a25f3 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1t9RQe-002Feh-T1@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From 92abfcb4ced482afbe65d18980e6734fe1e62a34 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1t9RQk-002Fen-1A@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
-
+++ /dev/null
-From f0f46c2a3d8ea9d1427298c8103a777d9e616c29 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1t9RQp-002Fet-5W@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
-
+++ /dev/null
-From d1a16dbbd84e02d2a6dcfcb8d5c4b8b2c0289f00 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1t9RQu-002Fez-AA@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
- }
-
+++ /dev/null
-From bc08ce37d99a3992e975a0f397503cb23404f25a Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1t9RQz-002Ff5-EA@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From 17ed1911f9c8d4f9af8e13b2c95103ee06dadc0f Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUrP-006ITh-6u@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From 1f92ead7e15003f632b5f138e8138095e0997d3d Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUrU-006ITn-Ai@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From 4e7d000286fe8e12f2d88032711ffab3ab658b12 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUrZ-006ITt-Fa@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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) {
+++ /dev/null
-From b4c7698dd95f253c6958d8c6ac219098009bf28a Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUre-006ITz-KF@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From c64c7fa0a774d9da72071a8517e359992baac982 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Acked-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/E1tIUrj-006IU6-ON@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 1c86828dff88e28b8ade6bddeee0163a023faf91 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUro-006IUC-Rq@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 5d58a890c02770ba8d790b1f3c6e8c0e20514dc2 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUru-006IUI-08@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From a219912e0fec73c346e64ef47013cb2e152f88fc Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUrz-006IUO-3r@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From df874f9e52c340cc6f0a0014a97b778f67d46849 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUs4-006IUU-7K@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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.
- */
+++ /dev/null
-From 513e8fb8fa32035b3325e2e14fb9598f8cb545e9 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUs9-006IUb-Au@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <PortType> = 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,
+++ /dev/null
-From d4169f0c7665afb8d8adb5e1b1df3db88517d0ad Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUsE-006IUh-E7@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- .../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 <PortType> = 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,
+++ /dev/null
-From 5fd0f1a02e750e2db4038dee60edea669ce5aab1 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUsJ-006IUn-H3@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From 77ac9a8b2536e0eaca6c6f21070068458bf55981 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tIUsO-006IUt-KN@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
-
+++ /dev/null
-From 6561f0e547be221f411fda5eddfcc5bd8bb058a5 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <maxime.chevallier@bootlin.com>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tJ8NM-006L5J-AH@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 520d29bdda86915b3caf8c72825a574bff212553 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tJ8NR-006L5P-E3@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 484d0170d6c6bbb5213d037664e9a551f793bacd Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/E1tJ8NW-006L5V-I9@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 1bd905dfea9897eafef532000702e63a66849f54 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/E1uslws-00000001SP5-1R2R@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
-
+++ /dev/null
-From a21202743f9ce4063e86b99cccaef48ef9813379 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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 <matt@traverse.com.au>
-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) <rmk+kernel@armlinux.org.uk>
-Tested-by: Vladimir Oltean <vladimir.oltean@nxp.com>
-Link: https://patch.msgid.link/E1uslwx-00000001SPB-2kiM@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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");
+++ /dev/null
-From f12b363887c706c40611fba645265527a8415832 Mon Sep 17 00:00:00 2001
-From: Rosen Penev <rosenp@gmail.com>
-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 <rosenp@gmail.com>
-(for hellcreek driver)
-Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de>
-Link: https://patch.msgid.link/20241028044828.1639668-1-rosenp@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From c4f873c2b65c839ff5e7c996bd9ef5a1e7eae11a Mon Sep 17 00:00:00 2001
-From: Torben Nielsen <torben.nielsen@prevas.dk>
-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 <torben.nielsen@prevas.dk>
-Signed-off-by: Claus Stovgaard <claus.stovgaard@prevas.dk>
-Link: https://patch.msgid.link/20250217080503.1390282-1-claus.stovgaard@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
+++ /dev/null
-From e39d14a760c039af0653e3df967e7525413924a0 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250510092211.276541-1-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <linux/export.h>
- #include <linux/gpio.h>
- #include <linux/kernel.h>
-+#include <linux/math.h>
- #include <linux/module.h>
- #include <linux/platform_data/b53.h>
- #include <linux/phy.h>
-@@ -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
+++ /dev/null
-From 75f4f7b2b13008803f84768ff90396f9d7553221 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250602193953.1010487-4-jonas.gorski@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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 <linux/gpio.h>
- #include <linux/kernel.h>
- #include <linux/math.h>
-+#include <linux/minmax.h>
- #include <linux/module.h>
- #include <linux/platform_data/b53.h>
- #include <linux/phy.h>
-@@ -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) {
+++ /dev/null
-From ef07df397a621707903ef0d294a7df11f80cf206 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
-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 <noltari@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250614080000.1884236-3-noltari@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From c3cf059a4d419b9c888ce7e9952fa13ba7569b61 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
-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 <florian.fainelli@broadcom.com>
-Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
-Link: https://patch.msgid.link/20250614080000.1884236-4-noltari@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
+++ /dev/null
-From 0cbec9aef5a86194117a956546dc1aec95031f37 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
-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 <noltari@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250614080000.1884236-5-noltari@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From c45655386e532c85ff1d679fc2aa40b3aaff9916 Mon Sep 17 00:00:00 2001
-From: Florian Fainelli <f.fainelli@gmail.com>
-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 <f.fainelli@gmail.com>
-Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
-Link: https://patch.msgid.link/20250614080000.1884236-6-noltari@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <linux/bcm47xx_nvram.h>
---- 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)
+++ /dev/null
-From 9b6c767c312b4709e9aeb2314a6b47863e7fb72d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
-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 <noltari@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250614080000.1884236-7-noltari@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From e17813968b08b1b09bf80699223dea48851cbd07 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
-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 <florian.fainelli@broadcom.com>
-Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
-Link: https://patch.msgid.link/20250614080000.1884236-11-noltari@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From 651c9e71ffe44e99b5a9b011271c2117f0353b32 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
-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 <noltari@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250614080000.1884236-13-noltari@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
- *************************************************************************/
-
+++ /dev/null
-From be7a79145d85af1a9d65a45560b9243b13a67782 Mon Sep 17 00:00:00 2001
-From: Kyle Hendry <kylehendrydev@gmail.com>
-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 <kylehendrydev@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250724035300.20497-2-kylehendrydev@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From fcf02a462fab52fbfcb24e617dd940745afd0dff Mon Sep 17 00:00:00 2001
-From: Kyle Hendry <kylehendrydev@gmail.com>
-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 <kylehendrydev@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250724035300.20497-4-kylehendrydev@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From aed2aaa3c963f8aabbfa061a177022fee826ebfb Mon Sep 17 00:00:00 2001
-From: Kyle Hendry <kylehendrydev@gmail.com>
-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 <kylehendrydev@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250724035300.20497-5-kylehendrydev@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <linux/module.h>
- #include <linux/of.h>
- #include <linux/io.h>
-+#include <linux/mfd/syscon.h>
- #include <linux/platform_device.h>
- #include <linux/platform_data/b53.h>
-
- #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;
+++ /dev/null
-From c251304ab021ff21c77e83e0babcb9eb76f8787a Mon Sep 17 00:00:00 2001
-From: Kyle Hendry <kylehendrydev@gmail.com>
-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 <kylehendrydev@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250724035300.20497-6-kylehendrydev@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
-
+++ /dev/null
-From e8e13073dff7052b144d002bae2cfe9ddfa27e2a Mon Sep 17 00:00:00 2001
-From: Kyle Hendry <kylehendrydev@gmail.com>
-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 <kylehendrydev@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250724035300.20497-7-kylehendrydev@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
+++ /dev/null
-From 5ac00023852d960528a0c1d10ae6c17893fc4113 Mon Sep 17 00:00:00 2001
-From: Kyle Hendry <kylehendrydev@gmail.com>
-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 <kylehendrydev@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250724035300.20497-8-kylehendrydev@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <linux/mfd/syscon.h>
- #include <linux/platform_device.h>
- #include <linux/platform_data/b53.h>
-+#include <linux/regmap.h>
-
- #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,
+++ /dev/null
-From 7f95f04fe1903a31b61085e3ab1b4730f9d72941 Mon Sep 17 00:00:00 2001
-From: Kyle Hendry <kylehendrydev@gmail.com>
-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 <kylehendrydev@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250814002530.5866-2-kylehendrydev@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 61730ac10ba90c52563861a0119504f6a9be9868 Mon Sep 17 00:00:00 2001
-From: Kyle Hendry <kylehendrydev@gmail.com>
-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 <kylehendrydev@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250814002530.5866-3-kylehendrydev@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From 89eb9a62aed77b409663ba1eac152e8f758815b7 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250815201809.549195-1-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From 674b34c4c770551e916ae707829c7faea4782d3a Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20250905124507.59186-1-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From e57723fe536f040cc2635ec1545dd0a7919a321e Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251102100758.28352-4-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
-
+++ /dev/null
-From 762e7e174da91cf4babfe77e45bc6b67334b1503 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251109134635.243951-1-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
-
+++ /dev/null
-From a6e4fd38bf2f2e2363b61c27f4e6c49b14e4bb07 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251107080749.26936-2-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From 4a291fe7226736a465ddb3fa93c21fcef7162ec7 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251107080749.26936-3-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From bf6e9d2ae1dbafee53ec4ccd126595172e1e5278 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251107080749.26936-4-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
- }
+++ /dev/null
-From 1716be6db04af53bac9b869f01156a460595cf41 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251107080749.26936-5-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From e0c476f325a8c9b961a3d446c24d3c8ecae7d186 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251107080749.26936-6-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From a7e73339ad46ade76d29fb6cc7d7854222608c26 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251107080749.26936-7-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <linux/bcm47xx_nvram.h>
+++ /dev/null
-From 300f78e8b6b7be17c2c78afeded75be68acb1aa7 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251107080749.26936-8-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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))
-
+++ /dev/null
-From 2b3013ac03028a2364d8779719bb6bfbc0212435 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251107080749.26936-9-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
- *************************************************************************/
+++ /dev/null
-From 6f268e275c74dae0536e0b61982a8db25bcf4f16 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <florian.fainelli@broadcom.com>
-Tested-by: Álvaro Fernández Rojas <noltari@gmail.com>
-Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
-Link: https://patch.msgid.link/20251128080625.27181-2-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From 9316012dd01952f75e37035360138ccc786ef727 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <florian.fainelli@broadcom.com>
-Tested-by: Álvaro Fernández Rojas <noltari@gmail.com>
-Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
-Link: https://patch.msgid.link/20251128080625.27181-3-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 8e46aacea4264bcb8d4265fb07577afff58ae78d Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <florian.fainelli@broadcom.com>
-Tested-by: Álvaro Fernández Rojas <noltari@gmail.com>
-Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
-Link: https://patch.msgid.link/20251128080625.27181-4-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From 85132103f700b1340fc17df8a981509d17bf4872 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251128080625.27181-5-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From 3b08863469aa6028ac7c3120966f4e2f6051cf6b Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <florian.fainelli@broadcom.com>
-Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
-Link: https://patch.msgid.link/20251128080625.27181-6-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From d39514e6a2d14f57830d649e2bf03b49612c2f73 Mon Sep 17 00:00:00 2001
-From: Jonas Gorski <jonas.gorski@gmail.com>
-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 <jonas.gorski@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://patch.msgid.link/20251128080625.27181-7-jonas.gorski@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From: Qingfang Deng <dqfext@gmail.com>
-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 <dqfext@gmail.com>
-Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
-Link: https://patch.msgid.link/20250301135517.695809-1-dqfext@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- 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) {
+++ /dev/null
-From 1a3e9b7a6b09e8ab3d2af019e4a392622685855e Mon Sep 17 00:00:00 2001
-From: Qingfang Deng <dqfext@gmail.com>
-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 <dqfext@gmail.com>
-Link: https://patch.msgid.link/20250610083211.909015-1-dqfext@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From b8844aab519a154808dbce15a132f3e8f1c34af6 Mon Sep 17 00:00:00 2001
-From: Qingfang Deng <dqfext@gmail.com>
-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 <dqfext@gmail.com>
-Link: https://patch.msgid.link/20250822012548.6232-1-dqfext@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From 72cdc67e7fa74931b055df3a76852bab551f1a04 Mon Sep 17 00:00:00 2001
-From: Qingfang Deng <dqfext@gmail.com>
-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 <dqfext@gmail.com>
-Reviewed-by: Eric Dumazet <edumazet@google.com>
-Link: https://patch.msgid.link/20250828012018.15922-1-dqfext@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From 4f54dff818d7b5b1d84becd5d90bc46e6233c0d7 Mon Sep 17 00:00:00 2001
-From: Qingfang Deng <dqfext@gmail.com>
-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 <dqfext@gmail.com>
-Reviewed-by: Eric Dumazet <edumazet@google.com>
-Link: https://patch.msgid.link/20250828012018.15922-2-dqfext@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From 6e56a6d47a7fad705a1a1d088237b0858c01a770 Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <o.rempel@pengutronix.de>
-Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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;
+++ /dev/null
-From 0b567519d1152de52b29b2da2c47aa0f39a46266 Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <andrew@lunn.ch>
-Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Reviewed-by: Kyle Swenson <kyle.swenson@est.tech>
-Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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)
+++ /dev/null
-From 4c2bab507eb7edc8e497e91b9b7f05d76d7e32bb Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <o.rempel@pengutronix.de>
-Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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 */
+++ /dev/null
-From f3cb3c7bea0c08e821d8e9dfd2f96acd1db7c24e Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <o.rempel@pengutronix.de>
-Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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)
+++ /dev/null
-From 3e9dbfec499807767d03592ebdf19d9c15fd495b Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <kory.maincent@bootlin.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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,
+++ /dev/null
-From 4640a1f0d8f2246f34d6e74330d7e7d2cf75605b Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <o.rempel@pengutronix.de>
-Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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);
+++ /dev/null
-From 7f076ce3f17334964590c2cce49a02c0851c099a Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <o.rempel@pengutronix.de>
-Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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";
+++ /dev/null
-From 10276f3e1c7e7f5de9f0bba58f8a849cb195253d Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <o.rempel@pengutronix.de>
-Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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)) {
+++ /dev/null
-From 5385f1e1923ca8131eb143567d509b101a344e06 Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <kory.maincent@bootlin.com>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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 <linux/device.h>
-+#include <linux/ethtool.h>
- #include <linux/of.h>
- #include <linux/pse-pd/pse.h>
- #include <linux/regulator/driver.h>
---- 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 <linux/ethtool.h>
- #include <linux/list.h>
- #include <uapi/linux/ethtool.h>
-
-@@ -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.
+++ /dev/null
-From fa2f0454174c2f33005f5a6e6f70c7160a15b2a1 Mon Sep 17 00:00:00 2001
-From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-1-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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);
- }
+++ /dev/null
-# 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)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-2-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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 <linux/device.h>
- #include <linux/ethtool.h>
-+#include <linux/ethtool_netlink.h>
- #include <linux/of.h>
-+#include <linux/phy.h>
- #include <linux/pse-pd/pse.h>
- #include <linux/regulator/driver.h>
- #include <linux/regulator/machine.h>
-+#include <linux/rtnetlink.h>
-+#include <net/net_trackers.h>
-
- 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 <linux/list.h>
- #include <uapi/linux/ethtool.h>
-+#include <uapi/linux/ethtool_netlink_generated.h>
-+#include <linux/regulator/driver.h>
-
- /* 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);
+++ /dev/null
-From f5e7aecaa4efcd4c85477b6a62f94fea668031db Mon Sep 17 00:00:00 2001
-From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-3-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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 <linux/pse-pd/pse.h>
-
- #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;
- }
-
+++ /dev/null
-From 50f8b341d26826aa5fdccb8f497fbff2500934b3 Mon Sep 17 00:00:00 2001
-From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-4-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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 <linux/rtnetlink.h>
- #include <net/net_trackers.h>
-
-+#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;
- };
-
- /**
+++ /dev/null
-# 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)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-5-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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))
+++ /dev/null
-From c394e757dedd9cf947f9ac470d615d28fd2b07d1 Mon Sep 17 00:00:00 2001
-From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-6-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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;
+++ /dev/null
-From ffef61d6d27374542f1bce4452200d9bdd2e1edd Mon Sep 17 00:00:00 2001
-From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-7-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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 <linux/list.h>
-+#include <linux/netlink.h>
-+#include <linux/kfifo.h>
- #include <uapi/linux/ethtool.h>
- #include <uapi/linux/ethtool_netlink_generated.h>
- #include <linux/regulator/driver.h>
-@@ -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)
- {
+++ /dev/null
-From 42d7c87b4e1251f36eceac987e74623e7cda8577 Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <kory.maincent@bootlin.com>
-Link: https://patch.msgid.link/20250115-feature_regulator_pw_budget-v2-1-0a44b949e6bc@bootlin.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
-Signed-off-by: Bevan Weiss <bevan.weiss@gmail.com>
----
- 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 */
+++ /dev/null
-From 359754013e6a7fc81af6735ebbfedd4a01999f68 Mon Sep 17 00:00:00 2001
-From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-9-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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 <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/pse-pd/pse.h>
-+#include <linux/regulator/driver.h>
-+#include <linux/regulator/machine.h>
-
- #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,
+++ /dev/null
-From 24a4e3a05dd0eadd0c9585c411880e5dcb6be97f Mon Sep 17 00:00:00 2001
-From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-10-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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;
+++ /dev/null
-From 56cfc97635e9164395c9242f72746454347155ab Mon Sep 17 00:00:00 2001
-From: "Kory Maincent (Dent Project)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-12-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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;
- }
+++ /dev/null
-From d12b3dc106090b358fb67b7c0c717a0884327ddf Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-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 <arnd@arndb.de>
-Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
-Link: https://patch.msgid.link/20250709153210.1920125-1-arnd@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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;
+++ /dev/null
-From 1c67f9c54cdc70627e3f6472b89cd3d895df974c Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <kory.maincent@bootlin.com>
-Link: https://patch.msgid.link/20250820132708.837255-1-kory.maincent@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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);
- }
-
+++ /dev/null
-From 7ef353879f714602b43f98662069f4fb86536761 Mon Sep 17 00:00:00 2001
-From: Kory Maincent <kory.maincent@bootlin.com>
-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 <kory.maincent@bootlin.com>
-Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250820133321.841054-1-kory.maincent@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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;
+++ /dev/null
-From a2317231df4b22e6634fe3d8645e7cef848acf49 Mon Sep 17 00:00:00 2001
-From: Piotr Kubik <piotr.kubik@adtran.com>
-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 <piotr.kubik@adtran.com>
-Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
-Link: https://patch.msgid.link/9b72c8cd-c8d3-4053-9c80-671b9481d166@adtran.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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 <linux/i2c.h>
-+#include <linux/module.h>
-+#include <linux/of.h>
-+#include <linux/platform_device.h>
-+#include <linux/pse-pd/pse.h>
-+
-+#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 <piotr.kubik@adtran.com>");
-+MODULE_DESCRIPTION("Skyworks Si3474 PoE PSE Controller driver");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From 2c95a756e0cfc19af6d0b32b0c6cf3bada334998 Mon Sep 17 00:00:00 2001
-From: Thomas Wismer <thomas.wismer@scs.ch>
-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 <thomas.wismer@scs.ch>
-Acked-by: Kory Maincent <kory.maincent@bootlin.com>
-Link: https://patch.msgid.link/20251006204029.7169-2-thomas@wismer.xyz
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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
-
+++ /dev/null
-From a496d2f0fd612ab9e10700afe00dc9267bad788b Mon Sep 17 00:00:00 2001
-From: Shengyu Qu <wiagn233@outlook.com>
-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 <nbd@nbd.name>
-Closes: https://github.com/openwrt/openwrt/issues/15857#issuecomment-2662851243
-Signed-off-by: Shengyu Qu <wiagn233@outlook.com>
-Reviewed-by: Ido Schimmel <idosch@nvidia.com>
-Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
-Link: https://patch.msgid.link/OSZPR01MB8434308370ACAFA90A22980798B32@OSZPR01MB8434.jpnprd01.prod.outlook.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
- }
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: OpenWrt PSE-PD Backport <openwrt@openwrt.org>
-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 <openwrt@openwrt.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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 */
+++ /dev/null
-# 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)" <kory.maincent@bootlin.com>
-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) <kory.maincent@bootlin.com>
-Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
-Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-8-78a1a645e2ee@bootlin.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
----
- 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] ||
+++ /dev/null
-From 5c964c8a97c12145104f5d2782aa1ffccf3a93dd Mon Sep 17 00:00:00 2001
-From: Martin Hou <martin.hou@foxmail.com>
-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 <martin.hou@foxmail.com>
-Link: https://patch.msgid.link/tencent_17DDD787B48E8A5AB8379ED69E23A0CD9309@qq.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 */
+++ /dev/null
-From fd8318a32573d73eb20637a0c80689de0dc98169 Mon Sep 17 00:00:00 2001
-From: Peng Fan <peng.fan@nxp.com>
-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 <peng.fan@nxp.com>
-Link: https://patch.msgid.link/20250103-wake_irq-v2-1-e3aeff5e9966@nxp.com
-Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
----
- 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 */
+++ /dev/null
-From 8cae5a0d91fea01d90ce7c1827e26934a22ca2fa Mon Sep 17 00:00:00 2001
-From: Rui Salvaterra <rsalvaterra@gmail.com>
-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 <rsalvaterra@gmail.com>
----
- 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;
+++ /dev/null
-From 277f96c1f3f71d6e1d3bcf650d7cd84c1442210f Mon Sep 17 00:00:00 2001
-From: "SkyLake.Huang" <skylake.huang@mediatek.com>
-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 <skylake.huang@mediatek.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
----
- 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
+++ /dev/null
-From c0dc1b412f9d840c51c5ee8927bf066e15a59550 Mon Sep 17 00:00:00 2001
-From: "SkyLake.Huang" <skylake.huang@mediatek.com>
-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 <skylake.huang@mediatek.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
----
- 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;
- }
+++ /dev/null
-From bcbbfb4f62c4ba35783cc617997a2e92d91e3940 Mon Sep 17 00:00:00 2001
-From: "SkyLake.Huang" <skylake.huang@mediatek.com>
-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 <skylake.huang@mediatek.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
----
- 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;
+++ /dev/null
-From e062f073dc0df4fcd338043cb0b69b6bcd31e4af Mon Sep 17 00:00:00 2001
-From: "SkyLake.Huang" <skylake.huang@mediatek.com>
-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 <andrew@lunn.ch>
-Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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 <dqfext@gmail.com>
- M: SkyLake Huang <SkyLake.Huang@mediatek.com>
- 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 <linux/bitfield.h>
--#include <linux/bitmap.h>
--#include <linux/mfd/syscon.h>
--#include <linux/module.h>
--#include <linux/nvmem-consumer.h>
--#include <linux/pinctrl/consumer.h>
--#include <linux/phy.h>
--#include <linux/regmap.h>
--
--#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 <daniel@makrotopia.org>");
--MODULE_AUTHOR("SkyLake Huang <SkyLake.Huang@mediatek.com>");
--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 <linux/bitfield.h>
--#include <linux/module.h>
--#include <linux/phy.h>
--
--#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 <dqfext@gmail.com>");
--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 <linux/bitfield.h>
-+#include <linux/bitmap.h>
-+#include <linux/mfd/syscon.h>
-+#include <linux/module.h>
-+#include <linux/nvmem-consumer.h>
-+#include <linux/pinctrl/consumer.h>
-+#include <linux/phy.h>
-+#include <linux/regmap.h>
-+
-+#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 <daniel@makrotopia.org>");
-+MODULE_AUTHOR("SkyLake Huang <SkyLake.Huang@mediatek.com>");
-+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 <linux/bitfield.h>
-+#include <linux/module.h>
-+#include <linux/phy.h>
-+
-+#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 <dqfext@gmail.com>");
-+MODULE_LICENSE("GPL");
-+
-+MODULE_DEVICE_TABLE(mdio, mtk_gephy_tbl);
+++ /dev/null
-From 71d88c7409b91c853d7f9c933f5e27933d656e5e Mon Sep 17 00:00:00 2001
-From: "SkyLake.Huang" <skylake.huang@mediatek.com>
-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 <andrew@lunn.ch>
-Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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 <SkyLake.Huang@mediatek
- L: netdev@vger.kernel.org
- S: Maintained
- F: drivers/net/phy/mediatek/mtk-ge-soc.c
-+F: drivers/net/phy/mediatek/mtk-phy-lib.c
- F: drivers/net/phy/mediatek/mtk-ge.c
-+F: drivers/net/phy/mediatek/mtk.h
- F: drivers/phy/mediatek/phy-mtk-xfi-tphy.c
-
- MEDIATEK I2C CONTROLLER DRIVER
---- a/drivers/net/phy/mediatek/Kconfig
-+++ b/drivers/net/phy/mediatek/Kconfig
-@@ -1,4 +1,7 @@
- # SPDX-License-Identifier: GPL-2.0-only
-+config MTK_NET_PHYLIB
-+ tristate
-+
- config MEDIATEK_GE_PHY
- tristate "MediaTek Gigabit Ethernet PHYs"
- help
-@@ -13,6 +16,7 @@ config MEDIATEK_GE_SOC_PHY
- tristate "MediaTek SoC Ethernet PHYs"
- depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST
- depends on NVMEM_MTK_EFUSE
-+ select MTK_NET_PHYLIB
- help
- Supports MediaTek SoC built-in Gigabit Ethernet PHYs.
-
---- a/drivers/net/phy/mediatek/Makefile
-+++ b/drivers/net/phy/mediatek/Makefile
-@@ -1,3 +1,4 @@
- # SPDX-License-Identifier: GPL-2.0
-+obj-$(CONFIG_MTK_NET_PHYLIB) += mtk-phy-lib.o
- obj-$(CONFIG_MEDIATEK_GE_PHY) += mtk-ge.o
- obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mtk-ge-soc.o
---- a/drivers/net/phy/mediatek/mtk-ge-soc.c
-+++ b/drivers/net/phy/mediatek/mtk-ge-soc.c
-@@ -8,6 +8,8 @@
- #include <linux/phy.h>
- #include <linux/regmap.h>
-
-+#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 <linux/phy.h>
-+#include <linux/module.h>
-+
-+#include <linux/netdevice.h>
-+
-+#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 <SkyLake.Huang@mediatek.com>");
-+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
-+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 <SkyLake.Huang@mediatek.com>
-+ * 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_ */
+++ /dev/null
-From 3efd0595fc7aaae300f5d9f4f0ae86f432c8d2c7 Mon Sep 17 00:00:00 2001
-From: "SkyLake.Huang" <skylake.huang@mediatek.com>
-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 <andrew@lunn.ch>
-Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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)
+++ /dev/null
-From 50a97d716105a5f35aaecca0bdfe8e23cba0e87f Mon Sep 17 00:00:00 2001
-From: "SkyLake.Huang" <skylake.huang@mediatek.com>
-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 <skylake.huang@mediatek.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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 <linux/module.h>
- #include <linux/phy.h>
-
-+#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);
+++ /dev/null
-From e6579df175d5b1baa605c82f8e759542262637cf Mon Sep 17 00:00:00 2001
-From: "SkyLake.Huang" <skylake.huang@mediatek.com>
-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 <andrew@lunn.ch>
-Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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) },
- { }
- };
-
+++ /dev/null
-From e127f7380aaf2cd1614961d826a4af7ab297d37f Mon Sep 17 00:00:00 2001
-From: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
-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 <christophe.jaillet@wanadoo.fr>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/403c381b7d9156b67ad68ffc44b8eee70c5e86a9.1736691226.git.christophe.jaillet@wanadoo.fr
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 },
+++ /dev/null
-From 7e06c3dbfa5f1e39eba92eb79d854fab2a7ad5fe Mon Sep 17 00:00:00 2001
-From: Sky Huang <skylake.huang@mediatek.com>
-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 <skylake.huang@mediatek.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250213080553.921434-2-SkyLake.Huang@mediatek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From 6e7370079669b0d55c9464bb7c3fb8fb7368b912 Mon Sep 17 00:00:00 2001
-From: Sky Huang <skylake.huang@mediatek.com>
-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 <skylake.huang@mediatek.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250213080553.921434-3-SkyLake.Huang@mediatek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
-
+++ /dev/null
-From c7e2fb3421ef5ebbb4c91f44bd735ab10edd755a Mon Sep 17 00:00:00 2001
-From: Sky Huang <skylake.huang@mediatek.com>
-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 <skylake.huang@mediatek.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250213080553.921434-4-SkyLake.Huang@mediatek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From 7851c73a416b15aff6f9ada9c88affc5f48ff011 Mon Sep 17 00:00:00 2001
-From: Sky Huang <skylake.huang@mediatek.com>
-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 <skylake.huang@mediatek.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250213080553.921434-5-SkyLake.Huang@mediatek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From bae8c61522c4d5a5250a24dcb57d120ea593fab1 Mon Sep 17 00:00:00 2001
-From: Sky Huang <skylake.huang@mediatek.com>
-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 <skylake.huang@mediatek.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250213080553.921434-6-SkyLake.Huang@mediatek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
-
+++ /dev/null
-From e5566162af8b9690e096d2e6089e4ed955a0d13d Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Acked-by: Arnd Bergmann <arnd@arndb.de>
-Link: https://patch.msgid.link/20250410100410.348-1-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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.
+++ /dev/null
-From 4590c8bc10951feee3e439bf7fff1b458c2e6fad Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Link: https://patch.msgid.link/20250410100410.348-2-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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) },
- { }
- };
-
+++ /dev/null
-From 34501d047ac0a6cbb13285ba9d15f75c1deb7da7 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <horms@kernel.org>
-Fixes: 6a325aed130b ("net: phy: mediatek: add Airoha PHY ID to SoC driver")
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Link: https://patch.msgid.link/20250415105313.3409-1-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From 952d7325362ffbefa6ce5619fb4e53c2159ec7a7 Mon Sep 17 00:00:00 2001
-From: Qingfang Deng <dqfext@gmail.com>
-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 <dqfext@gmail.com>
----
- 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;
+++ /dev/null
-From a2e1ba275eae96a8171deb19e9c7c2f5978fee7b Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/9ed760ff87d5fc456f31e407ead548bbb754497d.1728058550.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <linux/module.h>
- #include <linux/delay.h>
- #include <linux/bitfield.h>
-+#include <linux/of.h>
- #include <linux/phy.h>
-
- #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);
+++ /dev/null
-From ce21b8fb255ebf0b49913fb4c62741d7eb05c6f6 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <jonathanh@nvidia.com>
-Closes: https://lore.kernel.org/all/114b4c03-5d16-42ed-945d-cf78eabea12b@nvidia.com/
-Suggested-by: Hans-Frieder Vogt <hfdevel@gmx.net>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
----
- 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)
+++ /dev/null
-From a274465cc3bef2dfd9c9ea5100848dda0a8641e1 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/91598487773d768f254d5faf06cf65b13e972f0e.1728558223.git.daniel@makrotopia.org
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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,
+++ /dev/null
-From 9d55e68b19f222e6334ef4021c5527998f5ab537 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/86a413b4387c42dcb54f587cc2433a06f16aae83.1728558223.git.daniel@makrotopia.org
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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;
- }
+++ /dev/null
-From 78997e9a5e4d8a4df561e083a92c91ae23010e07 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/b6ec9050339f8244ff898898a1cecc33b13a48fc.1727741563.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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),
+++ /dev/null
-From f95b4725e796b12e5f347a0d161e1d3843142aa8 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/cc5da0a989af8b0d49d823656d88053c4de2ab98.1728057367.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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) |
+++ /dev/null
-From eb89c79c1b8f17fc1611540768678e60df89ac42 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/180ccafa837f09908b852a8a874a3808c5ecd2d0.1728558223.git.daniel@makrotopia.org
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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[] = {
+++ /dev/null
-From 1758af47b98c17da464cb45f476875150955dd48 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/81f4717ab9acf38f3239727a4540ae96fd01109b.1728558223.git.daniel@makrotopia.org
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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);
+++ /dev/null
-From 081156ce13f8fa4e97b5148dc54d8c0ddf02117b Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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)" <linux@armlinux.org.uk>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://patch.msgid.link/71fccf3f56742116eb18cc070d2a9810479ea7f9.1763650701.git.daniel@makrotopia.org
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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;
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-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 <rex.lu@mediatek.com>
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://patch.msgid.link/20250812-mt7992-wed-support-v3-1-9ada78a819a4@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- 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;
-
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-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 <pawlik.dan@gmail.com>
-Tested-by: Matteo Croce <teknoraver@meta.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://patch.msgid.link/20251008-wed-fixes-v1-1-8f7678583385@kernel.org
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- 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;
+++ /dev/null
-From: Rex Lu <rex.lu@mediatek.com>
-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 <pawlik.dan@gmail.com>
-Tested-by: Matteo Croce <teknoraver@meta.com>
-Signed-off-by: Rex Lu <rex.lu@mediatek.com>
-Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
-
---- 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);
+++ /dev/null
-From cff865c700711ecc3824b2dfe181637f3ed23c80 Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-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 <arnd@arndb.de>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20241217081056.238792-1-arnd@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 7b590490e3aa6bfa38bf6e2069a529017fd3c1d2 Mon Sep 17 00:00:00 2001
-From: Linus Walleij <linus.walleij@linaro.org>
-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 = <LED_COLOR_ID_GREEN>;
- function = LED_FUNCTION_LAN;
- default-state = "off";
- linux,default-trigger = "netdev";
- };
- led@1 {
- reg = <1>;
- color = <LED_COLOR_ID_AMBER>;
- 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 <linus.walleij@linaro.org>
----
- 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 <linux/of_irq.h>
- #include <linux/of_mdio.h>
- #include <linux/platform_data/mv88e6xxx.h>
-+#include <linux/property.h>
- #include <linux/netdevice.h>
- #include <linux/gpio/consumer.h>
- #include <linux/phylink.h>
-@@ -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 <linux/irq.h>
- #include <linux/gpio/consumer.h>
- #include <linux/kthread.h>
-+#include <linux/leds.h>
- #include <linux/phy.h>
-+#include <linux/property.h>
- #include <linux/ptp_clock_kernel.h>
- #include <linux/timecounter.h>
- #include <net/dsa.h>
-@@ -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 <linux/bitfield.h>
-+#include <linux/leds.h>
-+#include <linux/property.h>
-+
-+#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 <linux/if_bridge.h>
- #include <linux/phy.h>
- #include <linux/phylink.h>
-+#include <linux/property.h>
-
- #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);
+++ /dev/null
-From b8ee7a11c75436b85fa1641aa5f970de0f8a575c Mon Sep 17 00:00:00 2001
-From: Javier Carrasco <javier.carrasco.cruz@gmail.com>
-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 <javier.carrasco.cruz@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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");
- }
+++ /dev/null
-From f63e7c8a83892781f6ceb55566f9497639c44555 Mon Sep 17 00:00:00 2001
-From: Miaoqian Lin <linmq006@gmail.com>
-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 <linmq006@gmail.com>
-Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
-Link: https://patch.msgid.link/20250901073224.2273103-1-linmq006@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
+++ /dev/null
-From ae1c658b33d4bec20c037aebba583a68375d4773 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <andrew@lunn.ch>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Link: https://patch.msgid.link/20250911130840.23569-1-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From 854d71c555dfc3383c1fde7d9989b6046e21093d Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/382d8c88-cbce-400f-ad62-fda0181c7e38@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
- }
-
+++ /dev/null
-From b8bf38440ba94e8ed8e2ae55c5dfb0276d30e843 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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;
-
+++ /dev/null
-From e3fc5139bd8ffaa1498adc21be4e8ecbc6aed508 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/58e0da73-a7dd-4be3-82ae-d5b3f9069bde@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
- }
-
+++ /dev/null
-From ac48430368c1a4f4e6c2fa92243b4b93fd25bee4 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
----
- 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)
+++ /dev/null
-From e2015942e90a021151a5751776f35830ba063be7 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
----
- 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);
-
+++ /dev/null
-From 1c105bacb160b5918e917ab811552b7be69fc69c Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Message-ID: <d9c5094c-89a6-40e2-b5fe-8df7df4624ef@gmail.com>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
----
- 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;
- }
+++ /dev/null
-From d64113c6bb5ea5a70b7c9c3a6bcadef307638187 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Message-ID: <fb8c490c-2d92-48f5-8bbf-1fc1f2ee1649@gmail.com>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
----
- 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)
+++ /dev/null
-From c4e64095c00cb2de413cd6b90be047c273bcd491 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Message-ID: <95dd5a0c-09ea-4847-94d9-b7aa3063e8ff@gmail.com>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
----
- 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);
+++ /dev/null
-From b8bd8c44a266c9a7dcb907eab10fbb119e3f6494 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <lkp@intel.com>
-Closes: https://lore.kernel.org/oe-kbuild-all/202410220413.1gAxIJ4t-lkp@intel.com/
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/20fd6f39-3c1b-4af0-9adc-7d1f49728fad@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
- }
-
+++ /dev/null
-From eb90f876b7961d702d7fc549e14614860f531e60 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/044c925e-8669-4b98-87df-95b4056f4f5f@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- .../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,
+++ /dev/null
-From 4af2f60bf7378bd5c92b15a528d8c6c7d02bed6c Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/a8a9d896-fbe6-41f2-bf87-666567d3cdb3@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From a3d8520e6a19ab018da6c7fc22512c913697a829 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/71e4859e-4cd0-4b6b-b7fa-621d7721992f@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 2cd02f2fdd8a92e5b6b85ff64eab0fc549b30c07 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/3bf2f340-b369-4174-97bf-fd38d4217492@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From 83cb4b470c66b37b19a347a35cea01e0cbdd258d Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/680f2606-ac7d-4ced-8694-e5033855da9b@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
-
+++ /dev/null
-From c507e96b5763b36b63ad50ad804341f72ea000e4 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/697b197a-8eac-40c6-8847-27093cacec36@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From 330dc2297c82953dff402e0b4176a5383a618538 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/e1ccdb85-a4ed-4800-89c2-89770ff06452@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From e3e9e9039fa6ae885c7d5c954d7b9f105fa23e8f Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/51130715-45be-4db5-abb7-05d87e1f5df9@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From 7a3bcd39ae1f0e3ab896d9df62339ab4297a0bfd Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/3df1d484-a02e-46e7-8f75-db5b428e422e@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From e340bff27e63ed61a1e9895bed546107859e48a7 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/ce185e10-8a2f-4cf8-a49b-fd8fb3c3c8a1@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 2e20bf8cc05766dcd0357cdfcada49e1bc45512b Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
-Link: https://patch.msgid.link/d9dd214b-3027-4f60-b0e8-6f34a0c76582@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
- }
- }
-
+++ /dev/null
-From bb18265c3aba92b91a1355609769f3e967b65dee Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/b689ab6d-20b5-4b64-bd7e-531a0a972ba3@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From b299ea0069284186b0d3d54aebe87f0d195d457a Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/6a354364-20e9-48ad-a198-468264288757@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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])
+++ /dev/null
-From b3593df26ab19f114d613693fa8a92ab202803d0 Mon Sep 17 00:00:00 2001
-From: ChunHao Lin <hau@realtek.com>
-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 <hau@realtek.com>
-[hkallweit1@gmail.com: rebased after adjusted version numbering]
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/75e5e9ec-d01f-43ac-b0f4-e7456baf18d1@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
- };
+++ /dev/null
-From b11bff90f2ad52c5c55c822ecd20326619a73898 Mon Sep 17 00:00:00 2001
-From: ChunHao Lin <hau@realtek.com>
-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 <hau@realtek.com>
-Reviewed-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/20250107064355.104711-1-hau@realtek.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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,
- };
+++ /dev/null
-From 135c3c86a7cef4ba3d368da15b16c275b74582d3 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/d29f0cdb-32bf-435f-b59d-dc96bca1e3ab@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From d30460f42675fef5cd4b44ffbc49b545524555e3 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/9db73e9b-e2e8-45de-97a5-041c5f71d774@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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) },
- {}
- };
-
+++ /dev/null
-From 853e80369cfceb2331bf34f251ba11c6602cc67f Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/d6f97eaa-0f13-468f-89cb-75a41087bc4a@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From 473367a5ffe1607a61be481e2feda684eb5faea9 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <rsalvaterra@gmail.com>
-Tested-by: Rui Salvaterra <rsalvaterra@gmail.com>
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/396762ad-cc65-4e60-b01e-8847db89e98b@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
+++ /dev/null
-From 34e5ededf4b8ad4c9e58f0cab8596e26c8fa59a2 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/a36b4cf3-c792-40fa-8164-5dc9d5f14dd0@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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)
+++ /dev/null
-From 3d9b8ac5341269d31e59fd5d58d47266ac78bc32 Mon Sep 17 00:00:00 2001
-From: ChunHao Lin <hau@realtek.com>
-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 <hau@realtek.com>
-Reviewed-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/20250318083721.4127-2-hau@realtek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
-
+++ /dev/null
-From 8c40d99e5f43e0545a3f4fea9156313847e2eb79 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/b368fd91-57d7-4cb5-9342-98b4d8fe9aea@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From 0c49baf099ba2147a6ff3bbdc3197c6ddbee5469 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/847b7356-12d6-441b-ade9-4b6e1539b84a@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- .../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);
-
+++ /dev/null
-From 2b065c098c37a3ed28df7c3be59dca61b9da8402 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
-Link: https://patch.msgid.link/1fea533a-dd5a-4198-a9e2-895e11083947@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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",
+++ /dev/null
-From fe733618b27a8c033f0d246c2efff56fca322656 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/06991f47-2aec-4aa2-8918-2c6e79332303@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From b7ed5d5a78fccee96cf8919ac2c7a064c2f4c45b Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
-Link: https://patch.msgid.link/f573fdbd-ba6d-41c1-b68f-311d3c88db2c@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From 4dec0702b8627c364a0e1f2c5b249e06709a1c24 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/97d7ae79-d021-4b6b-b424-89e5e305b029@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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])
+++ /dev/null
-From f372ef6ed5a6b0401c884561d4bba1843e54d46a Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/0baad123-c679-4154-923f-fdc12783e900@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
- };
+++ /dev/null
-From 4f51e7d370a04122fa78470b031d6487c52298b1 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/ae866b71-c904-434e-befb-848c831e33ff@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From f24f7b2f3af9e008ded20f804d7829ee2efd43f2 Mon Sep 17 00:00:00 2001
-From: ChunHao Lin <hau@realtek.com>
-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 <hau@realtek.com>
-Reviewed-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/20250515095303.3138-1-hau@realtek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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])
+++ /dev/null
-From 081c9c0265c91b8333165aa6230c20bcbc6f7cbf Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Link: https://patch.msgid.link/b9a76341da851a18c985bc4774fa295babec79bb.1728565530.git.daniel@makrotopia.org
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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;
- }
+++ /dev/null
-From 68d5cd09e8919679ce13b85950debea4b2e98e04 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://patch.msgid.link/b15929a41621d215c6b2b57393368086589569ec.1728565530.git.daniel@makrotopia.org
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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)
+++ /dev/null
-From 5cb409b3960e75467cbb0a8e1e5596b4490570e3 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Link: https://patch.msgid.link/9dc9b47b2d675708afef3ad366bfd78eb584d958.1728565530.git.daniel@makrotopia.org
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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,
+++ /dev/null
-From 3d483a10327f38595f714f9f9e9dde43a622cb0f Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/e821b302-5fe6-49ab-aabd-05da500581c0@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From 34d5a86ff7bbe225fba3ad91f9b4dc85fb408e18 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Reviewed-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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;
+++ /dev/null
-From ea8318cb33e593bbfc59d637eae45a69732c5387 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Reviewed-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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);
+++ /dev/null
-From d3eb58549842c60ed46f37da7f4da969e3d6ecd3 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-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 <daniel@makrotopia.org>
-Reviewed-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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)
+++ /dev/null
-From 1416a9b2ba710d31954131c06d46f298e340aa2c Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/c566551b-c915-4e34-9b33-129a6ddd6e4c@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <r58129@freescale.com>
-- *
-- * Copyright (c) 2004 Freescale Semiconductor, Inc.
-- */
--#include <linux/bitops.h>
--#include <linux/of.h>
--#include <linux/phy.h>
--#include <linux/module.h>
--#include <linux/delay.h>
--#include <linux/clk.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)
--#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 <r58129@freescale.com>
-+ *
-+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
-+ */
-+#include <linux/bitops.h>
-+#include <linux/of.h>
-+#include <linux/phy.h>
-+#include <linux/module.h>
-+#include <linux/delay.h>
-+#include <linux/clk.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)
-+#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
-From 33700ca45b7d2e1655d4cad95e25671e8a94e2f0 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/ad6bfe9f-6375-4a00-84b4-bfb38a21bd71@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <linux/phy.h>
-+
-+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 <hkallweit1@gmail.com>
-+ */
-+
-+#include <linux/hwmon.h>
-+#include <linux/phy.h>
-+
-+#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 <linux/delay.h>
- #include <linux/clk.h>
-
-+#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,
+++ /dev/null
-From 64ff63aeefb03139ae27454bd4208244579ae88e Mon Sep 17 00:00:00 2001
-From: Aleksander Jan Bajkowski <olek2@wp.pl>
-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 <olek2@wp.pl>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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,
+++ /dev/null
-From 51773846fab24a353bed4ebb660997ced4bc32d7 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <geert@linux-m68k.org>
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/3466ee92-166a-4b0f-9ae7-42b9e046f333@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From 0bea93fdbaf8675b7e8124bdcaf51497dcc8bcfa Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <lkp@intel.com>
-Closes: https://lore.kernel.org/oe-kbuild-all/202501190707.qQS8PGHW-lkp@intel.com/
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- 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 <linux/module.h>
- #include <linux/delay.h>
- #include <linux/clk.h>
-+#include <linux/string_choices.h>
-
- #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) {
+++ /dev/null
-From da681ed73fb980286fc29de707b35d76bb33e123 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/a5f2333c-dda9-48ad-9801-77049766e632@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
+++ /dev/null
-From 02d3b306ac2f0b174753d1c5b9e4e5fb8ec5057e Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/81416f95-0fac-4225-87b4-828e3738b8ed@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
-
+++ /dev/null
-From 8af2136e77989a64fae0284bf76fd584e32edd3a Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/6344277b-c5c7-449b-ac89-d5425306ca76@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 {
+++ /dev/null
-From fabcfd6d10999024a721ae1b965b57eb8a305ace Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/c90bdf76-f8b8-4d06-9656-7a52d5658ee6@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
-
+++ /dev/null
-From bfc17c1658353f22843c7c13e27c2d31950f1887 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/E1ttnHW-00785s-Uq@rmk-PC.armlinux.org.uk
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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);
+++ /dev/null
-From 7840e4d6f48a75413470935ebdc4bab4fc0c035e Mon Sep 17 00:00:00 2001
-From: Daniel Braunwarth <daniel.braunwarth@kuka.com>
-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 <daniel.braunwarth@kuka.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250429-realtek_wol-v2-1-8f84def1ef2c@kuka.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <linux/bitops.h>
- #include <linux/of.h>
- #include <linux/phy.h>
-+#include <linux/netdevice.h>
- #include <linux/module.h>
- #include <linux/delay.h>
- #include <linux/clk.h>
-@@ -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,
+++ /dev/null
-From f3b265358b911fe9e495619bdfa7797749474f95 Mon Sep 17 00:00:00 2001
-From: Michael Klein <michael@fossekall.de>
-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 <michael@fossekall.de>
-Link: https://patch.msgid.link/20250504172916.243185-2-michael@fossekall.de
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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)
+++ /dev/null
-From 7c6fa3ffd2650347b1d37f028e232e53d617c1af Mon Sep 17 00:00:00 2001
-From: Michael Klein <michael@fossekall.de>
-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 <michael@fossekall.de>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250504172916.243185-3-michael@fossekall.de
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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)
+++ /dev/null
-From 12d40df259e38851a0d973535e6023b33e2ea4f9 Mon Sep 17 00:00:00 2001
-From: Michael Klein <michael@fossekall.de>
-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 <michael@fossekall.de>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250504172916.243185-4-michael@fossekall.de
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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));
+++ /dev/null
-From 8c4d0172657c1f2d86b9c19172150abcd0e35c39 Mon Sep 17 00:00:00 2001
-From: Michael Klein <michael@fossekall.de>
-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 <michael@fossekall.de>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250504172916.243185-5-michael@fossekall.de
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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)
+++ /dev/null
-From be1cc96ddf82bb0c0a159751f73239d6d3e9594a Mon Sep 17 00:00:00 2001
-From: Michael Klein <michael@fossekall.de>
-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 <michael@fossekall.de>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250504172916.243185-6-michael@fossekall.de
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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;
+++ /dev/null
-From 708686132ba02659267c0cebcc414348ece389a5 Mon Sep 17 00:00:00 2001
-From: Michael Klein <michael@fossekall.de>
-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 <michael@fossekall.de>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250504172916.243185-7-michael@fossekall.de
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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,
- }, {
+++ /dev/null
-From 83d9623161283f5f6883ee3fdb88ef2177b8a5f1 Mon Sep 17 00:00:00 2001
-From: ChunHao Lin <hau@realtek.com>
-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 <hau@realtek.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250516055622.3772-1-hau@realtek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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:
+++ /dev/null
-From 34167f1a024d2c5abae0b0325a6d0b8257160f86 Mon Sep 17 00:00:00 2001
-From: Markus Stockhausen <markus.stockhausen@gmx.de>
-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 <markus.stockhausen@gmx.de>
-Link: https://patch.msgid.link/20250813054407.1108285-1-markus.stockhausen@gmx.de
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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",
+++ /dev/null
-From 3a752e67800106a5c42d802d67e06c60aa71d07b Mon Sep 17 00:00:00 2001
-From: Markus Stockhausen <markus.stockhausen@gmx.de>
-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 <markus.stockhausen@gmx.de>
-Link: https://patch.msgid.link/20250815082009.3678865-1-markus.stockhausen@gmx.de
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
- }, {
+++ /dev/null
-From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Benno Lossin <lossin@kernel.org> # for Rust
-Reviewed-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
-Link: https://patch.msgid.link/20250517201353.5137-2-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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<T: Driver> Adapter<T> {
- /// `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
+++ /dev/null
-From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Link: https://patch.msgid.link/20250517201353.5137-5-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
+++ /dev/null
-From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Link: https://patch.msgid.link/20250517201353.5137-6-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <ansuelsmth@gmail.com>
-+L: netdev@vger.kernel.org
-+S: Maintained
-+F: drivers/net/phy/as21xxx.c
-+
- AF8133J THREE-AXIS MAGNETOMETER DRIVER
- M: Ondřej Jirman <megi@xff.cz>
- 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 <ansuelsmth@gmail.com>
-+ */
-+
-+#include <linux/bitfield.h>
-+#include <linux/firmware.h>
-+#include <linux/module.h>
-+#include <linux/of.h>
-+#include <linux/phy.h>
-+
-+#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 <ansuelsmth@gmail.com>");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From 9c370f7f92b0232fe925ca2caefb5e3126a0357d Mon Sep 17 00:00:00 2001
-From: Linus Walleij <linus.walleij@linaro.org>
-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 <linus.walleij@linaro.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250813-ks8995-to-dsa-v1-1-75c359ede3a5@linaro.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <juhosg at openwrt.org>
-+ *
-+ * This file was based on: drivers/spi/at25.c
-+ * Copyright (C) 2006 David Brownell
-+ */
-+
-+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-+
-+#include <linux/types.h>
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/delay.h>
-+#include <linux/device.h>
-+#include <linux/gpio/consumer.h>
-+#include <linux/of.h>
-+
-+#include <linux/spi/spi.h>
-+
-+#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 <juhosg at openwrt.org>");
-+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 <juhosg at openwrt.org>
-- *
-- * This file was based on: drivers/spi/at25.c
-- * Copyright (C) 2006 David Brownell
-- */
--
--#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
--
--#include <linux/types.h>
--#include <linux/kernel.h>
--#include <linux/module.h>
--#include <linux/delay.h>
--#include <linux/device.h>
--#include <linux/gpio/consumer.h>
--#include <linux/of.h>
--
--#include <linux/spi/spi.h>
--
--#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 <juhosg at openwrt.org>");
--MODULE_LICENSE("GPL v2");
+++ /dev/null
-From 7367bfc8b34d7ad5b0c06e19fe24533db22ed8ea Mon Sep 17 00:00:00 2001
-From: Linus Walleij <linus.walleij@linaro.org>
-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 <linus.walleij@linaro.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250813-ks8995-to-dsa-v1-2-75c359ede3a5@linaro.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
-
+++ /dev/null
-From 0e8d3f3623a45f69b6641ff9921ecd0c5afaa4ca Mon Sep 17 00:00:00 2001
-From: Linus Walleij <linus.walleij@linaro.org>
-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 <linus.walleij@linaro.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250813-ks8995-to-dsa-v1-3-75c359ede3a5@linaro.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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);
- }
+++ /dev/null
-From 4b486b63e07313e1e3807f9d5e6636aa564e0fbc Mon Sep 17 00:00:00 2001
-From: Linus Walleij <linus.walleij@linaro.org>
-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 <linus.walleij@linaro.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250813-ks8995-to-dsa-v1-4-75c359ede3a5@linaro.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <juhosg at openwrt.org>
-+ * Copyright (C) 2025 Linus Walleij <linus.walleij@linaro.org>
- *
- * 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 <linux/bits.h>
-+#include <linux/if_bridge.h>
-+#include <linux/if_vlan.h>
- #include <linux/types.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
-@@ -17,8 +21,8 @@
- #include <linux/device.h>
- #include <linux/gpio/consumer.h>
- #include <linux/of.h>
--
- #include <linux/spi/spi.h>
-+#include <net/dsa.h>
-
- #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);
- }
+++ /dev/null
-From a9b24b3583ae1da7dbda031f141264f2da260219 Mon Sep 17 00:00:00 2001
-From: Daniel Braunwarth <daniel.braunwarth@kuka.com>
-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 <daniel.braunwarth@kuka.com>
-Reported-by: Jon Hunter <jonathanh@nvidia.com>
-Closes: https://lore.kernel.org/baaa083b-9a69-460f-ab35-2a7cb3246ffd@nvidia.com
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250624-realtek_fixes-v1-1-02a0b7c369bc@kuka.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
-
+++ /dev/null
-From 2c67301584f2671e320236df6bbe75ae09feb4d0 Mon Sep 17 00:00:00 2001
-From: Marek Vasut <marek.vasut@mailbox.org>
-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 <marek.vasut@mailbox.org>
-Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://patch.msgid.link/20251011110309.12664-1-marek.vasut@mailbox.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From b826bf795564ddef6402cf2cb522ae035bd117ae Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/E1um8Ld-008jxD-Mc@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 <linux/bitops.h>
- #include <linux/of.h>
- #include <linux/phy.h>
-+#include <linux/pm_wakeirq.h>
- #include <linux/netdevice.h>
- #include <linux/module.h>
- #include <linux/delay.h>
-@@ -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,
+++ /dev/null
-From ffff5c8fc2af2218a3332b3d5b97654599d50cde Mon Sep 17 00:00:00 2001
-From: Aleksander Jan Bajkowski <olek2@wp.pl>
-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 <olek2@wp.pl>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://patch.msgid.link/20251016192325.2306757-1-olek2@wp.pl
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From f63f21e82ecafd288b100ea161247820bf1e92c4 Mon Sep 17 00:00:00 2001
-From: Aleksander Jan Bajkowski <olek2@wp.pl>
-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 <olek2@wp.pl>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250825211059.143231-1-olek2@wp.pl
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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,
+++ /dev/null
-From 8e982441ba601d982dd0739972115d85ae01d99b Mon Sep 17 00:00:00 2001
-From: Vladimir Oltean <vladimir.oltean@nxp.com>
-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 <vladimir.oltean@nxp.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20251117234033.345679-2-vladimir.oltean@nxp.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
-
+++ /dev/null
-From 27033d06917758d47162581da7e9de8004049dee Mon Sep 17 00:00:00 2001
-From: Vladimir Oltean <vladimir.oltean@nxp.com>
-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 <vladimir.oltean@nxp.com>
-Link: https://patch.msgid.link/20251117234033.345679-3-vladimir.oltean@nxp.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From 910ac7bfb1af1ae4cd141ef80e03a6729213c189 Mon Sep 17 00:00:00 2001
-From: Vladimir Oltean <vladimir.oltean@nxp.com>
-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 <vladimir.oltean@nxp.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20251117234033.345679-4-vladimir.oltean@nxp.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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 */
+++ /dev/null
-From e1a31c41bef678afe0d99b7f0dc3711a80c68447 Mon Sep 17 00:00:00 2001
-From: Vladimir Oltean <vladimir.oltean@nxp.com>
-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 <xiaoning.wang@nxp.com>
-Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
-Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20251117234033.345679-5-vladimir.oltean@nxp.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
-
+++ /dev/null
-From bb78b71faf60d11a15f07e3390fcfd31e5e523bb Mon Sep 17 00:00:00 2001
-From: Vladimir Oltean <vladimir.oltean@nxp.com>
-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 <vladimir.oltean@nxp.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20251117234033.345679-6-vladimir.oltean@nxp.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
- }
+++ /dev/null
-From 4465ae435ddc0162d5033a543658449d53d46d08 Mon Sep 17 00:00:00 2001
-From: Vladimir Oltean <vladimir.oltean@nxp.com>
-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 <vladimir.oltean@nxp.com>
-Link: https://patch.msgid.link/20251117234033.345679-7-vladimir.oltean@nxp.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From ae1737e7339b513f8c2fc21b500a0fc215d155c3 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <fabio.baltieri@gmail.com>
-Cc: stable@vger.kernel.org
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Link: https://patch.msgid.link/d7faae7e-66bc-404a-a432-3a496600575f@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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)
+++ /dev/null
-From 1abe21ef1adf0c5b6dbb5878c2fa4573df8d29fc Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://patch.msgid.link/20250823134431.4854-1-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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
+++ /dev/null
-From b4d5cd20507b252c746fa6971d82ac96f3b3e5b7 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-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 <ansuelsmth@gmail.com>
-Link: https://patch.msgid.link/20250823134431.4854-2-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- 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;
+++ /dev/null
-From 4f0638b12451112de4138689fa679315c8d388dc Mon Sep 17 00:00:00 2001
-From: Ivan Galkin <ivan.galkin@axis.com>
-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 <ivan.galkin@axis.com>
-Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
-Link: https://patch.msgid.link/20251202-phy_eee-v1-1-fe0bf6ab3df0@axis.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- 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);
+++ /dev/null
-From 79bc0af904db647979c735563299c9b0d820e432 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-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 <hkallweit1@gmail.com>
-Message-ID: <89690b81-2c73-47ae-9ae9-45c77b45ca0c@gmail.com>
-groeck: Renamed hwmon_ops_is_visible -> hwmon_is_visible
-Signed-off-by: Guenter Roeck <linux@roeck-us.net>
----
- 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,
+++ /dev/null
-From a4a7cbe36623ffaabbae413c0eacf40d033db71f Mon Sep 17 00:00:00 2001
-From: Heiko Stuebner <heiko@sntech.de>
-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 <heiko@sntech.de>
-Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
-Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
-Link: https://lore.kernel.org/r/20240906082511.2963890-2-heiko@sntech.de
-Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-
---- /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 <heiko@sntech.de>
-+
-+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>;
-+ };
-+...
+++ /dev/null
-From 6cb137c7e99f8307f1f0fcccb1896f2d3b0651d3 Mon Sep 17 00:00:00 2001
-From: Heiko Stuebner <heiko@sntech.de>
-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 <heiko@sntech.de>
-Link: https://lore.kernel.org/r/20240906082511.2963890-3-heiko@sntech.de
-Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-
---- 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
- */
+++ /dev/null
-From 36abe81d9c3fa200a57ef2363e93a2991e387e19 Mon Sep 17 00:00:00 2001
-From: Heiko Stuebner <heiko@sntech.de>
-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 <heiko@sntech.de>
-Link: https://lore.kernel.org/r/20240906082511.2963890-4-heiko@sntech.de
-Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-
---- 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);
+++ /dev/null
-From 4940071d962827467f7297be3b874c96186ca0b7 Mon Sep 17 00:00:00 2001
-From: Heiko Stuebner <heiko@sntech.de>
-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 <heiko@sntech.de>
-Link: https://lore.kernel.org/r/20240906082511.2963890-5-heiko@sntech.de
-Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-
---- a/drivers/clk/clk-gpio.c
-+++ b/drivers/clk/clk-gpio.c
-@@ -17,6 +17,7 @@
- #include <linux/device.h>
- #include <linux/of.h>
- #include <linux/platform_device.h>
-+#include <linux/regulator/consumer.h>
-
- /**
- * 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);
+++ /dev/null
-From b0fa00fe38f673c986633c11087274deeb7ce7b0 Mon Sep 17 00:00:00 2001
-From: Sander Vanheule <sander@svanheule.net>
-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 <sander@svanheule.net>
-Acked-by: Michael Walle <mwalle@kernel.org>
-Link: https://lore.kernel.org/r/20250107201621.12467-1-sander@svanheule.net
-Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
----
- 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;
+++ /dev/null
-From 5285b5ed04ab6ad40f7b654eefbccd6ae8cbf415 Mon Sep 17 00:00:00 2001
-From: Milan Krstic <milan.krstic@gmail.com>
-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 <mail@david-bauer.net>
-Signed-off-by: Milan Krstic <milan.krstic@gmail.com>
-Link: https://lore.kernel.org/20250703143039.5809-1-milan.krstic@gmail.com
-Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
----
- 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;
- }
+++ /dev/null
-From 77f5bb150132bbbcd6bc37ffdc80c9e140e373a4 Mon Sep 17 00:00:00 2001
-From: Kees Cook <kees@kernel.org>
-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 <kees@kernel.org>
-Link: https://lore.kernel.org/r/20250416222740.work.569-kees@kernel.org
-Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
-
---- 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, \
- }
+++ /dev/null
-From 599b92fd0efa8b7c43e7f58c9dd0f7951f7cbf09 Mon Sep 17 00:00:00 2001
-From: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
-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 <vicentiu.galanopulo@remote-tech.co.uk>
-Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Link: https://lore.kernel.org/r/20241218183401.41687-3-vicentiu.galanopulo@remote-tech.co.uk
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- .../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 <vicentiu.galanopulo@remote-tech.co.uk>
-+
-+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 <dt-bindings/leds/common.h>
-+
-+ 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 = <LED_COLOR_ID_RED>;
-+ function-enumerator = <1>;
-+ };
-+
-+ led@1 {
-+ reg = <0x1>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_GREEN>;
-+ function-enumerator = <2>;
-+ };
-+
-+ led@2 {
-+ reg = <0x2>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_BLUE>;
-+ function-enumerator = <3>;
-+ };
-+
-+ led@3 {
-+ reg = <0x3>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_RED>;
-+ function-enumerator = <4>;
-+ };
-+
-+ led@4 {
-+ reg = <0x4>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_GREEN>;
-+ function-enumerator = <5>;
-+ };
-+
-+ led@5 {
-+ reg = <0x5>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_BLUE>;
-+ function-enumerator = <6>;
-+ };
-+
-+ led@6 {
-+ reg = <0x6>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_RED>;
-+ function-enumerator = <7>;
-+ };
-+
-+ led@7 {
-+ reg = <0x7>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_GREEN>;
-+ function-enumerator = <8>;
-+ };
-+
-+ led@8 {
-+ reg = <0x8>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_BLUE>;
-+ function-enumerator = <9>;
-+ };
-+ };
-+ };
-+...
+++ /dev/null
-From 939757aafeb9c266dda37657ee5f7a73ffd35ae2 Mon Sep 17 00:00:00 2001
-From: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
-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 <vicentiu.galanopulo@remote-tech.co.uk>
-Link: https://lore.kernel.org/r/20241218183401.41687-4-vicentiu.galanopulo@remote-tech.co.uk
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- 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 <linux/cleanup.h>
-+#include <linux/ctype.h>
-+#include <linux/delay.h>
-+#include <linux/err.h>
-+#include <linux/gpio.h>
-+#include <linux/i2c.h>
-+#include <linux/leds.h>
-+#include <linux/module.h>
-+#include <linux/slab.h>
-+#include <linux/string.h>
-+
-+#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");
+++ /dev/null
-From c72e455b89f216b43cd0dbb518036ec4c98f5c46 Mon Sep 17 00:00:00 2001
-From: Manuel Fombuena <fombuena@outlook.com>
-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 <fombuena@outlook.com>
-Link: https://lore.kernel.org/r/CWLP123MB54732771AC0CE5491B3C84DCC5C32@CWLP123MB5473.GBRP123.PROD.OUTLOOK.COM
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- 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;
+++ /dev/null
-From 35fa2d88ca9481e5caf533d58b99ca259c63b2fe Mon Sep 17 00:00:00 2001
-From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-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 <Jonathan.Cameron@huawei.com>
-Reviewed-by: Danilo Krummrich <dakr@kernel.org>
-Reviewed-by: Lyude Paul <lyude@redhat.com>
-Reviewed-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
-Reviewed-by: Zijun Hu <quic_zijuhu@quicinc.com>
-Link: https://lore.kernel.org/r/2025021026-atlantic-gibberish-3f0c@gregkh
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- 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 <gregkh@linuxfoundation.org>
-+ * 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 <linux/err.h>
-+#include <linux/init.h>
-+#include <linux/slab.h>
-+#include <linux/string.h>
-+#include <linux/container_of.h>
-+#include <linux/device/faux.h>
-+#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 <gregkh@linuxfoundation.org>
-+ * 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 <linux/container_of.h>
-+#include <linux/device.h>
-+
-+/**
-+ * 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_ */