generic: 6.18: remove obsolete backport patches

Remove obsolete backport patches already included in kernel 6.18.

Signed-off-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
Link: https://github.com/openwrt/openwrt/pull/21078
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
Mieczyslaw Nalewaj 2026-03-27 17:48:03 +01:00 committed by Robert Marko
parent 141cb99b41
commit 5c73f28bfd
281 changed files with 0 additions and 48293 deletions

View File

@ -1,63 +0,0 @@
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);

View File

@ -1,60 +0,0 @@
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,

View File

@ -1,35 +0,0 @@
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)

View File

@ -1,199 +0,0 @@
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;
}

View File

@ -1,118 +0,0 @@
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)
{

View File

@ -1,61 +0,0 @@
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]);

View File

@ -1,85 +0,0 @@
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 */

View File

@ -1,163 +0,0 @@
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
/**

View File

@ -1,155 +0,0 @@
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); \
})
/*

View File

@ -1,32 +0,0 @@
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;
}

View File

@ -1,38 +0,0 @@
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 = {

View File

@ -1,124 +0,0 @@
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,
&macronix_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;

View File

@ -1,329 +0,0 @@
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);

View File

@ -1,28 +0,0 @@
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)),
};

View File

@ -1,880 +0,0 @@
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");

View File

@ -1,198 +0,0 @@
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

View File

@ -1,64 +0,0 @@
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);

View File

@ -1,77 +0,0 @@
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;
+
+ );
};
/*

View File

@ -1,28 +0,0 @@
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);

View File

@ -1,35 +0,0 @@
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);

View File

@ -1,49 +0,0 @@
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 = {

View File

@ -1,36 +0,0 @@
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;
}

View File

@ -1,35 +0,0 @@
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;
}

View File

@ -1,29 +0,0 @@
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);

View File

@ -1,30 +0,0 @@
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;

View File

@ -1,37 +0,0 @@
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/

View File

@ -1,166 +0,0 @@
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)

View File

@ -1,113 +0,0 @@
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;

View File

@ -1,81 +0,0 @@
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) |

View File

@ -1,31 +0,0 @@
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;

View File

@ -1,74 +0,0 @@
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

View File

@ -1,66 +0,0 @@
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;

View File

@ -1,56 +0,0 @@
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;

View File

@ -1,69 +0,0 @@
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,

View File

@ -1,88 +0,0 @@
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;

View File

@ -1,30 +0,0 @@
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

View File

@ -1,299 +0,0 @@
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.

View File

@ -1,48 +0,0 @@
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);

View File

@ -1,63 +0,0 @@
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,

View File

@ -1,98 +0,0 @@
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);

View File

@ -1,64 +0,0 @@
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 +=

View File

@ -1,81 +0,0 @@
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

View File

@ -1,201 +0,0 @@
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
&macronix_spinand_manufacturer,
&micron_spinand_manufacturer,
&paragon_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;

View File

@ -1,53 +0,0 @@
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)

View File

@ -1,94 +0,0 @@
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)

View File

@ -1,104 +0,0 @@
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;

View File

@ -1,200 +0,0 @@
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;
+}

View File

@ -1,41 +0,0 @@
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

View File

@ -1,42 +0,0 @@
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);

View File

@ -1,37 +0,0 @@
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);

View File

@ -1,127 +0,0 @@
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;
}
}

View File

@ -1,85 +0,0 @@
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;

View File

@ -1,95 +0,0 @@
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)

View File

@ -1,290 +0,0 @@
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;

View File

@ -1,66 +0,0 @@
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) {

View File

@ -1,118 +0,0 @@
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);

View File

@ -1,41 +0,0 @@
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,

View File

@ -1,63 +0,0 @@
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,

View File

@ -1,79 +0,0 @@
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);

View File

@ -1,77 +0,0 @@
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,

View File

@ -1,159 +0,0 @@
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.
*/

View File

@ -1,64 +0,0 @@
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,

View File

@ -1,62 +0,0 @@
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,

View File

@ -1,228 +0,0 @@
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)

View File

@ -1,110 +0,0 @@
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;

View File

@ -1,53 +0,0 @@
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,

View File

@ -1,47 +0,0 @@
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,

View File

@ -1,58 +0,0 @@
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,

View File

@ -1,117 +0,0 @@
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);

View File

@ -1,68 +0,0 @@
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");

View File

@ -1,30 +0,0 @@
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)

View File

@ -1,76 +0,0 @@
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;
}

View File

@ -1,105 +0,0 @@
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

View File

@ -1,70 +0,0 @@
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) {

View File

@ -1,188 +0,0 @@
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

View File

@ -1,48 +0,0 @@
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;
}

View File

@ -1,112 +0,0 @@
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

View File

@ -1,250 +0,0 @@
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, &reg);
+ b53_read8(dev, B53_ARLIO_PAGE, offset, &reg);
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)

View File

@ -1,51 +0,0 @@
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);

View File

@ -1,33 +0,0 @@
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)

View File

@ -1,128 +0,0 @@
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
*************************************************************************/

View File

@ -1,50 +0,0 @@
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,

View File

@ -1,124 +0,0 @@
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)

View File

@ -1,69 +0,0 @@
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;

View File

@ -1,47 +0,0 @@
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;
}

View File

@ -1,42 +0,0 @@
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;
}

View File

@ -1,100 +0,0 @@
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,

View File

@ -1,33 +0,0 @@
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,

View File

@ -1,77 +0,0 @@
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);

View File

@ -1,72 +0,0 @@
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);

View File

@ -1,77 +0,0 @@
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;

View File

@ -1,61 +0,0 @@
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);

View File

@ -1,59 +0,0 @@
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);

View File

@ -1,85 +0,0 @@
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)

View File

@ -1,117 +0,0 @@
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)

View File

@ -1,93 +0,0 @@
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);
}

View File

@ -1,85 +0,0 @@
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, &reg);
+ b53_read_arl_srch_ctl(dev, &reg);
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);

Some files were not shown because too many files have changed in this diff Show More