mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-05-04 09:31:26 +02:00
kernel/generic: restore files for v6.12
This is an automatically generated commit which aids following Kernel patch history, as git will see the move and copy as a rename thus defeating the purpose. For the original discussion see: https://lists.openwrt.org/pipermail/openwrt-devel/2023-October/041673.html Signed-off-by: Mieczyslaw Nalewaj <namiltd@yahoo.com> Link: https://github.com/openwrt/openwrt/pull/21078 Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
parent
b70d9a15af
commit
141cb99b41
@ -0,0 +1,62 @@
|
||||
From 06e92afca89075628b12c9b4085b4cc7320081ac Mon Sep 17 00:00:00 2001
|
||||
From: Jascha Sundaresan <flizarthanon@gmail.com>
|
||||
Date: Thu, 23 Oct 2025 03:07:41 +0400
|
||||
Subject: nvmem: layouts: u-boot-env: add optional "env-size" property
|
||||
|
||||
Some devices reserve a larger NVMEM region for the U-Boot environment
|
||||
than the actual environment data length used by U-Boot itself. The CRC32
|
||||
in the U-Boot header is calculated over the smaller data length, causing
|
||||
CRC validation to fail when Linux reads the full partition.
|
||||
|
||||
Allow an optional device tree property "env-size" to specify the
|
||||
environment data size to use for CRC computation.
|
||||
|
||||
v2: add missing $ref line to DT binding
|
||||
|
||||
Signed-off-by: Jascha Sundaresan <flizarthanon@gmail.com>
|
||||
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
|
||||
---
|
||||
Documentation/devicetree/bindings/nvmem/layouts/u-boot,env.yaml | 7 +++++++
|
||||
drivers/nvmem/layouts/u-boot-env.c | 4 +++-
|
||||
2 files changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/nvmem/layouts/u-boot,env.yaml
|
||||
+++ b/Documentation/devicetree/bindings/nvmem/layouts/u-boot,env.yaml
|
||||
@@ -46,6 +46,12 @@ properties:
|
||||
type: object
|
||||
description: Command to use for automatic booting
|
||||
|
||||
+ env-size:
|
||||
+ description:
|
||||
+ Size in bytes of the environment data used by U-Boot for CRC
|
||||
+ calculation. If omitted, the full NVMEM region size is used.
|
||||
+ $ref: /schemas/types.yaml#/definitions/uint32
|
||||
+
|
||||
ethaddr:
|
||||
type: object
|
||||
description: Ethernet interfaces base MAC address.
|
||||
@@ -104,6 +110,7 @@ examples:
|
||||
|
||||
partition-u-boot-env {
|
||||
compatible = "brcm,env";
|
||||
+ env-size = <0x20000>;
|
||||
|
||||
ethaddr {
|
||||
};
|
||||
--- a/drivers/nvmem/layouts/u-boot-env.c
|
||||
+++ b/drivers/nvmem/layouts/u-boot-env.c
|
||||
@@ -99,10 +99,12 @@ int u_boot_env_parse(struct device *dev,
|
||||
uint32_t crc32;
|
||||
uint32_t calc;
|
||||
uint8_t *buf;
|
||||
+ u32 env_size;
|
||||
int bytes;
|
||||
int err;
|
||||
|
||||
- dev_size = nvmem_dev_size(nvmem);
|
||||
+ dev_size = device_property_read_u32(dev, "env-size", &env_size) ?
|
||||
+ nvmem_dev_size(nvmem) : (size_t)env_size;
|
||||
|
||||
buf = kzalloc(dev_size, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
@ -0,0 +1,63 @@
|
||||
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);
|
||||
@ -0,0 +1,60 @@
|
||||
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,
|
||||
@ -0,0 +1,35 @@
|
||||
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)
|
||||
@ -0,0 +1,199 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@ -0,0 +1,118 @@
|
||||
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)
|
||||
{
|
||||
@ -0,0 +1,61 @@
|
||||
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]);
|
||||
@ -0,0 +1,85 @@
|
||||
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 */
|
||||
@ -0,0 +1,163 @@
|
||||
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
|
||||
|
||||
/**
|
||||
@ -0,0 +1,155 @@
|
||||
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); \
|
||||
})
|
||||
|
||||
/*
|
||||
@ -0,0 +1,103 @@
|
||||
From 2a6c045640c38a407a39cd40c3c4d8dd2fd89aa8 Mon Sep 17 00:00:00 2001
|
||||
From: Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
Date: Thu, 6 Nov 2025 14:34:00 +0100
|
||||
Subject: [PATCH 1/2] bitfield: Add less-checking __FIELD_{GET,PREP}()
|
||||
|
||||
The BUILD_BUG_ON_MSG() check against "~0ull" works only with "unsigned
|
||||
(long) long" _mask types. For constant masks, that condition is usually
|
||||
met, as GENMASK() yields an UL value. The few places where the
|
||||
constant mask is stored in an intermediate variable were fixed by
|
||||
changing the variable type to u64 (see e.g. [1] and [2]).
|
||||
|
||||
However, for non-constant masks, smaller unsigned types should be valid,
|
||||
too, but currently lead to "result of comparison of constant
|
||||
18446744073709551615 with expression of type ... is always
|
||||
false"-warnings with clang and W=1.
|
||||
|
||||
Hence refactor the __BF_FIELD_CHECK() helper, and factor out
|
||||
__FIELD_{GET,PREP}(). The later lack the single problematic check, but
|
||||
are otherwise identical to FIELD_{GET,PREP}(), and are intended to be
|
||||
used in the fully non-const variants later.
|
||||
|
||||
[1] commit 5c667d5a5a3ec166 ("clk: sp7021: Adjust width of _m in
|
||||
HWM_FIELD_PREP()")
|
||||
[2] commit cfd6fb45cfaf46fa ("crypto: ccree - avoid out-of-range
|
||||
warnings from clang")
|
||||
|
||||
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
Link: https://git.kernel.org/torvalds/c/5c667d5a5a3ec166 [1]
|
||||
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
|
||||
---
|
||||
include/linux/bitfield.h | 36 ++++++++++++++++++++++++++++--------
|
||||
1 file changed, 28 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/include/linux/bitfield.h
|
||||
+++ b/include/linux/bitfield.h
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
#define __bf_cast_unsigned(type, x) ((__unsigned_scalar_typeof(type))(x))
|
||||
|
||||
-#define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx) \
|
||||
+#define __BF_FIELD_CHECK_MASK(_mask, _val, _pfx) \
|
||||
({ \
|
||||
BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask), \
|
||||
_pfx "mask is not constant"); \
|
||||
@@ -69,13 +69,33 @@
|
||||
~((_mask) >> __bf_shf(_mask)) & \
|
||||
(0 + (_val)) : 0, \
|
||||
_pfx "value too large for the field"); \
|
||||
- BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
|
||||
- __bf_cast_unsigned(_reg, ~0ull), \
|
||||
- _pfx "type of reg too small for mask"); \
|
||||
__BUILD_BUG_ON_NOT_POWER_OF_2((_mask) + \
|
||||
(1ULL << __bf_shf(_mask))); \
|
||||
})
|
||||
|
||||
+#define __BF_FIELD_CHECK_REG(mask, reg, pfx) \
|
||||
+ BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \
|
||||
+ __bf_cast_unsigned(reg, ~0ull), \
|
||||
+ pfx "type of reg too small for mask")
|
||||
+
|
||||
+#define __BF_FIELD_CHECK(mask, reg, val, pfx) \
|
||||
+ ({ \
|
||||
+ __BF_FIELD_CHECK_MASK(mask, val, pfx); \
|
||||
+ __BF_FIELD_CHECK_REG(mask, reg, pfx); \
|
||||
+ })
|
||||
+
|
||||
+#define __FIELD_PREP(mask, val, pfx) \
|
||||
+ ({ \
|
||||
+ __BF_FIELD_CHECK_MASK(mask, val, pfx); \
|
||||
+ ((typeof(mask))(val) << __bf_shf(mask)) & (mask); \
|
||||
+ })
|
||||
+
|
||||
+#define __FIELD_GET(mask, reg, pfx) \
|
||||
+ ({ \
|
||||
+ __BF_FIELD_CHECK_MASK(mask, 0U, pfx); \
|
||||
+ (typeof(mask))(((reg) & (mask)) >> __bf_shf(mask)); \
|
||||
+ })
|
||||
+
|
||||
/**
|
||||
* FIELD_MAX() - produce the maximum value representable by a field
|
||||
* @_mask: shifted mask defining the field's length and position
|
||||
@@ -112,8 +132,8 @@
|
||||
*/
|
||||
#define FIELD_PREP(_mask, _val) \
|
||||
({ \
|
||||
- __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
|
||||
- ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
|
||||
+ __BF_FIELD_CHECK_REG(_mask, 0ULL, "FIELD_PREP: "); \
|
||||
+ __FIELD_PREP(_mask, _val, "FIELD_PREP: "); \
|
||||
})
|
||||
|
||||
#define __BF_CHECK_POW2(n) BUILD_BUG_ON_ZERO(((n) & ((n) - 1)) != 0)
|
||||
@@ -152,8 +172,8 @@
|
||||
*/
|
||||
#define FIELD_GET(_mask, _reg) \
|
||||
({ \
|
||||
- __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: "); \
|
||||
- (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
|
||||
+ __BF_FIELD_CHECK_REG(_mask, _reg, "FIELD_GET: "); \
|
||||
+ __FIELD_GET(_mask, _reg, "FIELD_GET: "); \
|
||||
})
|
||||
|
||||
extern void __compiletime_error("value doesn't fit into mask")
|
||||
@ -0,0 +1,114 @@
|
||||
From c1c6ab80b25c8db1e2ef5ae3ac8075d2c242ae13 Mon Sep 17 00:00:00 2001
|
||||
From: Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
Date: Thu, 6 Nov 2025 14:34:01 +0100
|
||||
Subject: [PATCH 2/2] bitfield: Add non-constant field_{prep,get}() helpers
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The existing FIELD_{GET,PREP}() macros are limited to compile-time
|
||||
constants. However, it is very common to prepare or extract bitfield
|
||||
elements where the bitfield mask is not a compile-time constant.
|
||||
|
||||
To avoid this limitation, the AT91 clock driver and several other
|
||||
drivers already have their own non-const field_{prep,get}() macros.
|
||||
Make them available for general use by adding them to
|
||||
<linux/bitfield.h>, and improve them slightly:
|
||||
1. Avoid evaluating macro parameters more than once,
|
||||
2. Replace "ffs() - 1" by "__ffs()",
|
||||
3. Support 64-bit use on 32-bit architectures,
|
||||
4. Wire field_{get,prep}() to FIELD_{GET,PREP}() when mask is
|
||||
actually constant.
|
||||
|
||||
This is deliberately not merged into the existing FIELD_{GET,PREP}()
|
||||
macros, as people expressed the desire to keep stricter variants for
|
||||
increased safety, or for performance critical paths.
|
||||
|
||||
Yury: use __mask withing new macros.
|
||||
|
||||
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
|
||||
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
|
||||
Acked-by: Crt Mori <cmo@melexis.com>
|
||||
Acked-by: Nuno Sá <nuno.sa@analog.com>
|
||||
Acked-by: Richard Genoud <richard.genoud@bootlin.com>
|
||||
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
|
||||
Reviewed-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
|
||||
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
|
||||
---
|
||||
include/linux/bitfield.h | 59 ++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 59 insertions(+)
|
||||
|
||||
--- a/include/linux/bitfield.h
|
||||
+++ b/include/linux/bitfield.h
|
||||
@@ -16,6 +16,7 @@
|
||||
* FIELD_{GET,PREP} macros take as first parameter shifted mask
|
||||
* from which they extract the base mask and shift amount.
|
||||
* Mask must be a compilation time constant.
|
||||
+ * field_{get,prep} are variants that take a non-const mask.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
@@ -223,4 +224,62 @@ __MAKE_OP(64)
|
||||
#undef __MAKE_OP
|
||||
#undef ____MAKE_OP
|
||||
|
||||
+#define __field_prep(mask, val) \
|
||||
+ ({ \
|
||||
+ __auto_type __mask = (mask); \
|
||||
+ typeof(__mask) __val = (val); \
|
||||
+ unsigned int __shift = BITS_PER_TYPE(__mask) <= 32 ? \
|
||||
+ __ffs(__mask) : __ffs64(__mask); \
|
||||
+ (__val << __shift) & __mask; \
|
||||
+ })
|
||||
+
|
||||
+#define __field_get(mask, reg) \
|
||||
+ ({ \
|
||||
+ __auto_type __mask = (mask); \
|
||||
+ typeof(__mask) __reg = (reg); \
|
||||
+ unsigned int __shift = BITS_PER_TYPE(__mask) <= 32 ? \
|
||||
+ __ffs(__mask) : __ffs64(__mask); \
|
||||
+ (__reg & __mask) >> __shift; \
|
||||
+ })
|
||||
+
|
||||
+/**
|
||||
+ * field_prep() - prepare a bitfield element
|
||||
+ * @mask: shifted mask defining the field's length and position, must be
|
||||
+ * non-zero
|
||||
+ * @val: value to put in the field
|
||||
+ *
|
||||
+ * Return: field value masked and shifted to its final destination
|
||||
+ *
|
||||
+ * field_prep() masks and shifts up the value. The result should be
|
||||
+ * combined with other fields of the bitfield using logical OR.
|
||||
+ * Unlike FIELD_PREP(), @mask is not limited to a compile-time constant.
|
||||
+ * Typical usage patterns are a value stored in a table, or calculated by
|
||||
+ * shifting a constant by a variable number of bits.
|
||||
+ * If you want to ensure that @mask is a compile-time constant, please use
|
||||
+ * FIELD_PREP() directly instead.
|
||||
+ */
|
||||
+#define field_prep(mask, val) \
|
||||
+ (__builtin_constant_p(mask) ? __FIELD_PREP(mask, val, "field_prep: ") \
|
||||
+ : __field_prep(mask, val))
|
||||
+
|
||||
+/**
|
||||
+ * field_get() - extract a bitfield element
|
||||
+ * @mask: shifted mask defining the field's length and position, must be
|
||||
+ * non-zero
|
||||
+ * @reg: value of entire bitfield
|
||||
+ *
|
||||
+ * Return: extracted field value
|
||||
+ *
|
||||
+ * field_get() extracts the field specified by @mask from the
|
||||
+ * bitfield passed in as @reg by masking and shifting it down.
|
||||
+ * Unlike FIELD_GET(), @mask is not limited to a compile-time constant.
|
||||
+ * Typical usage patterns are a value stored in a table, or calculated by
|
||||
+ * shifting a constant by a variable number of bits.
|
||||
+ * If you want to ensure that @mask is a compile-time constant, please use
|
||||
+ * FIELD_GET() directly instead.
|
||||
+ */
|
||||
+#define field_get(mask, reg) \
|
||||
+ (__builtin_constant_p(mask) ? __FIELD_GET(mask, reg, "field_get: ") \
|
||||
+ : __field_get(mask, reg))
|
||||
+
|
||||
#endif
|
||||
@ -0,0 +1,32 @@
|
||||
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;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
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 = {
|
||||
@ -0,0 +1,124 @@
|
||||
From 5f284dc15ca8695d0394414045ac64616a3b0e69 Mon Sep 17 00:00:00 2001
|
||||
From: Tianling Shen <cnsztl@gmail.com>
|
||||
Date: Mon, 25 Aug 2025 01:00:13 +0800
|
||||
Subject: [PATCH] mtd: spinand: add support for FudanMicro FM25S01A
|
||||
|
||||
Add support for FudanMicro FM25S01A SPI NAND.
|
||||
Datasheet: http://eng.fmsh.com/nvm/FM25S01A_ds_eng.pdf
|
||||
|
||||
Signed-off-by: Tianling Shen <cnsztl@gmail.com>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Link: https://lore.kernel.org/linux-mtd/20250824170013.3328777-1-cnsztl@gmail.com
|
||||
---
|
||||
drivers/mtd/nand/spi/Makefile | 2 +-
|
||||
drivers/mtd/nand/spi/core.c | 1 +
|
||||
drivers/mtd/nand/spi/fmsh.c | 74 +++++++++++++++++++++++++++++++++++
|
||||
include/linux/mtd/spinand.h | 1 +
|
||||
4 files changed, 77 insertions(+), 1 deletion(-)
|
||||
create mode 100644 drivers/mtd/nand/spi/fmsh.c
|
||||
|
||||
--- a/drivers/mtd/nand/spi/Makefile
|
||||
+++ b/drivers/mtd/nand/spi/Makefile
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
-spinand-objs := core.o alliancememory.o ato.o esmt.o foresee.o gigadevice.o macronix.o
|
||||
+spinand-objs := core.o alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o macronix.o
|
||||
spinand-objs += micron.o paragon.o toshiba.o winbond.o xtx.o
|
||||
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
|
||||
--- a/drivers/mtd/nand/spi/core.c
|
||||
+++ b/drivers/mtd/nand/spi/core.c
|
||||
@@ -1123,6 +1123,7 @@ static const struct spinand_manufacturer
|
||||
&alliancememory_spinand_manufacturer,
|
||||
&ato_spinand_manufacturer,
|
||||
&esmt_c8_spinand_manufacturer,
|
||||
+ &fmsh_spinand_manufacturer,
|
||||
&foresee_spinand_manufacturer,
|
||||
&gigadevice_spinand_manufacturer,
|
||||
¯onix_spinand_manufacturer,
|
||||
--- /dev/null
|
||||
+++ b/drivers/mtd/nand/spi/fmsh.c
|
||||
@@ -0,0 +1,74 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd.
|
||||
+ *
|
||||
+ * Author: Dingqiang Lin <jon.lin@rock-chips.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/device.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/mtd/spinand.h>
|
||||
+
|
||||
+#define SPINAND_MFR_FMSH 0xA1
|
||||
+
|
||||
+static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
|
||||
+
|
||||
+static SPINAND_OP_VARIANTS(write_cache_variants,
|
||||
+ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
|
||||
+ SPINAND_PROG_LOAD(true, 0, NULL, 0));
|
||||
+
|
||||
+static SPINAND_OP_VARIANTS(update_cache_variants,
|
||||
+ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
|
||||
+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
+
|
||||
+static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
+ struct mtd_oob_region *region)
|
||||
+{
|
||||
+ return -ERANGE;
|
||||
+}
|
||||
+
|
||||
+static int fm25s01a_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
+ struct mtd_oob_region *region)
|
||||
+{
|
||||
+ if (section)
|
||||
+ return -ERANGE;
|
||||
+
|
||||
+ region->offset = 2;
|
||||
+ region->length = 62;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
|
||||
+ .ecc = fm25s01a_ooblayout_ecc,
|
||||
+ .free = fm25s01a_ooblayout_free,
|
||||
+};
|
||||
+
|
||||
+static const struct spinand_info fmsh_spinand_table[] = {
|
||||
+ SPINAND_INFO("FM25S01A",
|
||||
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
|
||||
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
+ NAND_ECCREQ(1, 512),
|
||||
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
+ &write_cache_variants,
|
||||
+ &update_cache_variants),
|
||||
+ SPINAND_HAS_QE_BIT,
|
||||
+ SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
|
||||
+};
|
||||
+
|
||||
+static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
|
||||
+};
|
||||
+
|
||||
+const struct spinand_manufacturer fmsh_spinand_manufacturer = {
|
||||
+ .id = SPINAND_MFR_FMSH,
|
||||
+ .name = "Fudan Micro",
|
||||
+ .chips = fmsh_spinand_table,
|
||||
+ .nchips = ARRAY_SIZE(fmsh_spinand_table),
|
||||
+ .ops = &fmsh_spinand_manuf_ops,
|
||||
+};
|
||||
--- a/include/linux/mtd/spinand.h
|
||||
+++ b/include/linux/mtd/spinand.h
|
||||
@@ -263,6 +263,7 @@ struct spinand_manufacturer {
|
||||
extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer ato_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
|
||||
+extern const struct spinand_manufacturer fmsh_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer foresee_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
|
||||
@ -0,0 +1,329 @@
|
||||
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);
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
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)),
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,880 @@
|
||||
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");
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,198 @@
|
||||
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
|
||||
@ -0,0 +1,64 @@
|
||||
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);
|
||||
@ -0,0 +1,77 @@
|
||||
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;
|
||||
+
|
||||
+ );
|
||||
};
|
||||
|
||||
/*
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,28 @@
|
||||
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);
|
||||
@ -0,0 +1,35 @@
|
||||
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);
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
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 = {
|
||||
@ -0,0 +1,36 @@
|
||||
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;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
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;
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
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);
|
||||
@ -0,0 +1,30 @@
|
||||
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;
|
||||
@ -0,0 +1,37 @@
|
||||
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/
|
||||
@ -0,0 +1,166 @@
|
||||
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)
|
||||
@ -0,0 +1,113 @@
|
||||
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;
|
||||
@ -0,0 +1,81 @@
|
||||
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) |
|
||||
@ -0,0 +1,31 @@
|
||||
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;
|
||||
@ -0,0 +1,74 @@
|
||||
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
|
||||
@ -0,0 +1,66 @@
|
||||
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;
|
||||
@ -0,0 +1,56 @@
|
||||
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;
|
||||
@ -0,0 +1,69 @@
|
||||
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,
|
||||
@ -0,0 +1,88 @@
|
||||
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;
|
||||
@ -0,0 +1,30 @@
|
||||
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
|
||||
@ -0,0 +1,299 @@
|
||||
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.
|
||||
@ -0,0 +1,48 @@
|
||||
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);
|
||||
@ -0,0 +1,63 @@
|
||||
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,
|
||||
@ -0,0 +1,98 @@
|
||||
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);
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
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 +=
|
||||
@ -0,0 +1,81 @@
|
||||
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
|
||||
@ -0,0 +1,201 @@
|
||||
From 1a50e3612de9187857f55ee14a573f7f8e7d4ebc Mon Sep 17 00:00:00 2001
|
||||
From: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
|
||||
Date: Tue, 3 Dec 2024 11:46:50 +0900
|
||||
Subject: [PATCH] mtd: spinand: Add support for SkyHigh S35ML-3 family
|
||||
|
||||
SkyHigh S35ML01G300, S35ML01G301, S35ML02G300, and S35ML04G300 are 1Gb,
|
||||
2Gb, and 4Gb SLC SPI NAND flash family. This family of devices has
|
||||
on-die ECC which parity bits are stored to hidden area. In this family
|
||||
the on-die ECC cannot be disabled so raw access needs to be prevented.
|
||||
|
||||
Link: https://www.skyhighmemory.com/download/SPI_S35ML01_04G3_002_19205.pdf?v=P
|
||||
Co-developed-by: KR Kim <kr.kim@skyhighmemory.com>
|
||||
Signed-off-by: KR Kim <kr.kim@skyhighmemory.com>
|
||||
Signed-off-by: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
---
|
||||
drivers/mtd/nand/spi/Makefile | 2 +-
|
||||
drivers/mtd/nand/spi/core.c | 1 +
|
||||
drivers/mtd/nand/spi/skyhigh.c | 147 +++++++++++++++++++++++++++++++++
|
||||
include/linux/mtd/spinand.h | 1 +
|
||||
4 files changed, 150 insertions(+), 1 deletion(-)
|
||||
create mode 100644 drivers/mtd/nand/spi/skyhigh.c
|
||||
|
||||
--- a/drivers/mtd/nand/spi/Makefile
|
||||
+++ b/drivers/mtd/nand/spi/Makefile
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
spinand-objs := core.o alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o macronix.o
|
||||
-spinand-objs += micron.o paragon.o toshiba.o winbond.o xtx.o
|
||||
+spinand-objs += micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o
|
||||
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
|
||||
--- a/drivers/mtd/nand/spi/core.c
|
||||
+++ b/drivers/mtd/nand/spi/core.c
|
||||
@@ -1191,6 +1191,7 @@ static const struct spinand_manufacturer
|
||||
¯onix_spinand_manufacturer,
|
||||
µn_spinand_manufacturer,
|
||||
¶gon_spinand_manufacturer,
|
||||
+ &skyhigh_spinand_manufacturer,
|
||||
&toshiba_spinand_manufacturer,
|
||||
&winbond_spinand_manufacturer,
|
||||
&xtx_spinand_manufacturer,
|
||||
--- /dev/null
|
||||
+++ b/drivers/mtd/nand/spi/skyhigh.c
|
||||
@@ -0,0 +1,147 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Copyright (c) 2024 SkyHigh Memory Limited
|
||||
+ *
|
||||
+ * Author: Takahiro Kuwano <takahiro.kuwano@infineon.com>
|
||||
+ * Co-Author: KR Kim <kr.kim@skyhighmemory.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/device.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/mtd/spinand.h>
|
||||
+
|
||||
+#define SPINAND_MFR_SKYHIGH 0x01
|
||||
+#define SKYHIGH_STATUS_ECC_1TO2_BITFLIPS (1 << 4)
|
||||
+#define SKYHIGH_STATUS_ECC_3TO6_BITFLIPS (2 << 4)
|
||||
+#define SKYHIGH_STATUS_ECC_UNCOR_ERROR (3 << 4)
|
||||
+#define SKYHIGH_CONFIG_PROTECT_EN BIT(1)
|
||||
+
|
||||
+static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0),
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0),
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
|
||||
+ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
|
||||
+
|
||||
+static SPINAND_OP_VARIANTS(write_cache_variants,
|
||||
+ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
|
||||
+ SPINAND_PROG_LOAD(true, 0, NULL, 0));
|
||||
+
|
||||
+static SPINAND_OP_VARIANTS(update_cache_variants,
|
||||
+ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
|
||||
+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
+
|
||||
+static int skyhigh_spinand_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
+ struct mtd_oob_region *region)
|
||||
+{
|
||||
+ /* ECC bytes are stored in hidden area. */
|
||||
+ return -ERANGE;
|
||||
+}
|
||||
+
|
||||
+static int skyhigh_spinand_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
+ struct mtd_oob_region *region)
|
||||
+{
|
||||
+ if (section)
|
||||
+ return -ERANGE;
|
||||
+
|
||||
+ /* ECC bytes are stored in hidden area. Reserve 2 bytes for the BBM. */
|
||||
+ region->offset = 2;
|
||||
+ region->length = mtd->oobsize - 2;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct mtd_ooblayout_ops skyhigh_spinand_ooblayout = {
|
||||
+ .ecc = skyhigh_spinand_ooblayout_ecc,
|
||||
+ .free = skyhigh_spinand_ooblayout_free,
|
||||
+};
|
||||
+
|
||||
+static int skyhigh_spinand_ecc_get_status(struct spinand_device *spinand,
|
||||
+ u8 status)
|
||||
+{
|
||||
+ switch (status & STATUS_ECC_MASK) {
|
||||
+ case STATUS_ECC_NO_BITFLIPS:
|
||||
+ return 0;
|
||||
+
|
||||
+ case SKYHIGH_STATUS_ECC_UNCOR_ERROR:
|
||||
+ return -EBADMSG;
|
||||
+
|
||||
+ case SKYHIGH_STATUS_ECC_1TO2_BITFLIPS:
|
||||
+ return 2;
|
||||
+
|
||||
+ case SKYHIGH_STATUS_ECC_3TO6_BITFLIPS:
|
||||
+ return 6;
|
||||
+
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
+static const struct spinand_info skyhigh_spinand_table[] = {
|
||||
+ SPINAND_INFO("S35ML01G301",
|
||||
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15),
|
||||
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
+ NAND_ECCREQ(6, 32),
|
||||
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
+ &write_cache_variants,
|
||||
+ &update_cache_variants),
|
||||
+ SPINAND_NO_RAW_ACCESS,
|
||||
+ SPINAND_ECCINFO(&skyhigh_spinand_ooblayout,
|
||||
+ skyhigh_spinand_ecc_get_status)),
|
||||
+ SPINAND_INFO("S35ML01G300",
|
||||
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14),
|
||||
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
+ NAND_ECCREQ(6, 32),
|
||||
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
+ &write_cache_variants,
|
||||
+ &update_cache_variants),
|
||||
+ SPINAND_NO_RAW_ACCESS,
|
||||
+ SPINAND_ECCINFO(&skyhigh_spinand_ooblayout,
|
||||
+ skyhigh_spinand_ecc_get_status)),
|
||||
+ SPINAND_INFO("S35ML02G300",
|
||||
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25),
|
||||
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
|
||||
+ NAND_ECCREQ(6, 32),
|
||||
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
+ &write_cache_variants,
|
||||
+ &update_cache_variants),
|
||||
+ SPINAND_NO_RAW_ACCESS,
|
||||
+ SPINAND_ECCINFO(&skyhigh_spinand_ooblayout,
|
||||
+ skyhigh_spinand_ecc_get_status)),
|
||||
+ SPINAND_INFO("S35ML04G300",
|
||||
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
|
||||
+ NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 2, 1, 1),
|
||||
+ NAND_ECCREQ(6, 32),
|
||||
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
+ &write_cache_variants,
|
||||
+ &update_cache_variants),
|
||||
+ SPINAND_NO_RAW_ACCESS,
|
||||
+ SPINAND_ECCINFO(&skyhigh_spinand_ooblayout,
|
||||
+ skyhigh_spinand_ecc_get_status)),
|
||||
+};
|
||||
+
|
||||
+static int skyhigh_spinand_init(struct spinand_device *spinand)
|
||||
+{
|
||||
+ /*
|
||||
+ * Config_Protect_En (bit 1 in Block Lock register) must be set to 1
|
||||
+ * before writing other bits. Do it here before core unlocks all blocks
|
||||
+ * by writing block protection bits.
|
||||
+ */
|
||||
+ return spinand_write_reg_op(spinand, REG_BLOCK_LOCK,
|
||||
+ SKYHIGH_CONFIG_PROTECT_EN);
|
||||
+}
|
||||
+
|
||||
+static const struct spinand_manufacturer_ops skyhigh_spinand_manuf_ops = {
|
||||
+ .init = skyhigh_spinand_init,
|
||||
+};
|
||||
+
|
||||
+const struct spinand_manufacturer skyhigh_spinand_manufacturer = {
|
||||
+ .id = SPINAND_MFR_SKYHIGH,
|
||||
+ .name = "SkyHigh",
|
||||
+ .chips = skyhigh_spinand_table,
|
||||
+ .nchips = ARRAY_SIZE(skyhigh_spinand_table),
|
||||
+ .ops = &skyhigh_spinand_manuf_ops,
|
||||
+};
|
||||
--- a/include/linux/mtd/spinand.h
|
||||
+++ b/include/linux/mtd/spinand.h
|
||||
@@ -269,6 +269,7 @@ extern const struct spinand_manufacturer
|
||||
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer micron_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer paragon_spinand_manufacturer;
|
||||
+extern const struct spinand_manufacturer skyhigh_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer winbond_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer xtx_spinand_manufacturer;
|
||||
@ -0,0 +1,84 @@
|
||||
From b98994cb9bc24f5c7575c86650f96c384576fdfa Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Mon, 17 Nov 2025 02:54:19 +0000
|
||||
Subject: [PATCH] mtd: spinand: esmt: add support for F50L1G41LC
|
||||
|
||||
This adds support for ESMT F50L1G41LC, which appears to be an updated
|
||||
version of the already supported F50L1G41LB.
|
||||
Add esmt_8c SPI_NAND manufacturer to account for the newly used vendor
|
||||
ID with support for the ESMT F50L1G41LC chip.
|
||||
|
||||
Link: https://github.com/openwrt/openwrt/pull/15214#issuecomment-3514824435
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
---
|
||||
drivers/mtd/nand/spi/core.c | 1 +
|
||||
drivers/mtd/nand/spi/esmt.c | 24 ++++++++++++++++++++++++
|
||||
include/linux/mtd/spinand.h | 1 +
|
||||
3 files changed, 26 insertions(+)
|
||||
|
||||
--- a/drivers/mtd/nand/spi/core.c
|
||||
+++ b/drivers/mtd/nand/spi/core.c
|
||||
@@ -1184,6 +1184,7 @@ static const struct nand_ops spinand_ops
|
||||
static const struct spinand_manufacturer *spinand_manufacturers[] = {
|
||||
&alliancememory_spinand_manufacturer,
|
||||
&ato_spinand_manufacturer,
|
||||
+ &esmt_8c_spinand_manufacturer,
|
||||
&esmt_c8_spinand_manufacturer,
|
||||
&fmsh_spinand_manufacturer,
|
||||
&foresee_spinand_manufacturer,
|
||||
--- a/drivers/mtd/nand/spi/esmt.c
|
||||
+++ b/drivers/mtd/nand/spi/esmt.c
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
|
||||
#define SPINAND_MFR_ESMT_C8 0xc8
|
||||
+#define SPINAND_MFR_ESMT_8C 0x8c
|
||||
|
||||
static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
|
||||
@@ -102,6 +103,19 @@ static const struct mtd_ooblayout_ops f5
|
||||
.free = f50l1g41lb_ooblayout_free,
|
||||
};
|
||||
|
||||
+
|
||||
+static const struct spinand_info esmt_8c_spinand_table[] = {
|
||||
+ SPINAND_INFO("F50L1G41LC",
|
||||
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x2C),
|
||||
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
+ NAND_ECCREQ(1, 512),
|
||||
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
+ &write_cache_variants,
|
||||
+ &update_cache_variants),
|
||||
+ 0,
|
||||
+ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
|
||||
+};
|
||||
+
|
||||
static const struct spinand_info esmt_c8_spinand_table[] = {
|
||||
SPINAND_INFO("F50L1G41LB",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
|
||||
@@ -138,6 +152,14 @@ static const struct spinand_info esmt_c8
|
||||
static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
|
||||
};
|
||||
|
||||
+const struct spinand_manufacturer esmt_8c_spinand_manufacturer = {
|
||||
+ .id = SPINAND_MFR_ESMT_8C,
|
||||
+ .name = "ESMT",
|
||||
+ .chips = esmt_8c_spinand_table,
|
||||
+ .nchips = ARRAY_SIZE(esmt_8c_spinand_table),
|
||||
+ .ops = &esmt_spinand_manuf_ops,
|
||||
+};
|
||||
+
|
||||
const struct spinand_manufacturer esmt_c8_spinand_manufacturer = {
|
||||
.id = SPINAND_MFR_ESMT_C8,
|
||||
.name = "ESMT",
|
||||
--- a/include/linux/mtd/spinand.h
|
||||
+++ b/include/linux/mtd/spinand.h
|
||||
@@ -262,6 +262,7 @@ struct spinand_manufacturer {
|
||||
/* SPI NAND manufacturers */
|
||||
extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer ato_spinand_manufacturer;
|
||||
+extern const struct spinand_manufacturer esmt_8c_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer fmsh_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer foresee_spinand_manufacturer;
|
||||
@ -0,0 +1,115 @@
|
||||
From f6dffe2a9ed1bdcee1879e2728310fb1e08602cf Mon Sep 17 00:00:00 2001
|
||||
From: Mikhail Zhilkin <csharper2005@gmail.com>
|
||||
Date: Thu, 27 Nov 2025 22:59:00 +0300
|
||||
Subject: mtd: spinand: add support for FudanMicro FM25S01BI3
|
||||
|
||||
Add support for FudanMicro FM25S01BI3 SPI NAND.
|
||||
|
||||
Link: https://www.fmsh.com/nvm/FM25S01BI3_ds_eng.pdf
|
||||
|
||||
Signed-off-by: Mikhail Zhilkin <csharper2005@gmail.com>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
---
|
||||
drivers/mtd/nand/spi/fmsh.c | 72 +++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 72 insertions(+)
|
||||
|
||||
--- a/drivers/mtd/nand/spi/fmsh.c
|
||||
+++ b/drivers/mtd/nand/spi/fmsh.c
|
||||
@@ -9,6 +9,13 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/spinand.h>
|
||||
|
||||
+#define FM25S01BI3_STATUS_ECC_MASK (7 << 4)
|
||||
+ #define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4)
|
||||
+ #define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4)
|
||||
+ #define FM25S01BI3_STATUS_ECC_UNCOR_ERROR (2 << 4)
|
||||
+ #define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS (3 << 4)
|
||||
+ #define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS (5 << 4)
|
||||
+
|
||||
#define SPINAND_MFR_FMSH 0xA1
|
||||
|
||||
static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
@@ -45,11 +52,66 @@ static int fm25s01a_ooblayout_free(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand,
|
||||
+ u8 status)
|
||||
+{
|
||||
+ switch (status & FM25S01BI3_STATUS_ECC_MASK) {
|
||||
+ case FM25S01BI3_STATUS_ECC_NO_BITFLIPS:
|
||||
+ return 0;
|
||||
+
|
||||
+ case FM25S01BI3_STATUS_ECC_UNCOR_ERROR:
|
||||
+ return -EBADMSG;
|
||||
+
|
||||
+ case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS:
|
||||
+ return 3;
|
||||
+
|
||||
+ case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS:
|
||||
+ return 6;
|
||||
+
|
||||
+ case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS:
|
||||
+ return 8;
|
||||
+
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
+static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
+ struct mtd_oob_region *region)
|
||||
+{
|
||||
+ if (section)
|
||||
+ return -ERANGE;
|
||||
+
|
||||
+ region->offset = 64;
|
||||
+ region->length = 64;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
+ struct mtd_oob_region *region)
|
||||
+{
|
||||
+ if (section > 3)
|
||||
+ return -ERANGE;
|
||||
+
|
||||
+ region->offset = (16 * section) + 4;
|
||||
+ region->length = 12;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
|
||||
.ecc = fm25s01a_ooblayout_ecc,
|
||||
.free = fm25s01a_ooblayout_free,
|
||||
};
|
||||
|
||||
+static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = {
|
||||
+ .ecc = fm25s01bi3_ooblayout_ecc,
|
||||
+ .free = fm25s01bi3_ooblayout_free,
|
||||
+};
|
||||
+
|
||||
static const struct spinand_info fmsh_spinand_table[] = {
|
||||
SPINAND_INFO("FM25S01A",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
|
||||
@@ -60,6 +122,16 @@ static const struct spinand_info fmsh_sp
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
|
||||
+ SPINAND_INFO("FM25S01BI3",
|
||||
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4),
|
||||
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
+ NAND_ECCREQ(8, 512),
|
||||
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
+ &write_cache_variants,
|
||||
+ &update_cache_variants),
|
||||
+ SPINAND_HAS_QE_BIT,
|
||||
+ SPINAND_ECCINFO(&fm25s01bi3_ooblayout,
|
||||
+ fm25s01bi3_ecc_get_status)),
|
||||
};
|
||||
|
||||
static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
|
||||
@ -0,0 +1,53 @@
|
||||
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)
|
||||
@ -0,0 +1,94 @@
|
||||
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)
|
||||
@ -0,0 +1,104 @@
|
||||
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;
|
||||
@ -0,0 +1,200 @@
|
||||
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;
|
||||
+}
|
||||
@ -0,0 +1,41 @@
|
||||
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
|
||||
@ -0,0 +1,42 @@
|
||||
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);
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
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);
|
||||
|
||||
@ -0,0 +1,127 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
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;
|
||||
@ -0,0 +1,95 @@
|
||||
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)
|
||||
@ -0,0 +1,290 @@
|
||||
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;
|
||||
@ -0,0 +1,66 @@
|
||||
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) {
|
||||
@ -0,0 +1,118 @@
|
||||
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);
|
||||
@ -0,0 +1,41 @@
|
||||
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,
|
||||
@ -0,0 +1,63 @@
|
||||
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,
|
||||
@ -0,0 +1,79 @@
|
||||
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);
|
||||
@ -0,0 +1,77 @@
|
||||
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,
|
||||
@ -0,0 +1,159 @@
|
||||
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.
|
||||
*/
|
||||
@ -0,0 +1,64 @@
|
||||
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,
|
||||
@ -0,0 +1,62 @@
|
||||
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,
|
||||
@ -0,0 +1,228 @@
|
||||
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)
|
||||
@ -0,0 +1,110 @@
|
||||
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;
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
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,
|
||||
@ -0,0 +1,47 @@
|
||||
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,
|
||||
@ -0,0 +1,58 @@
|
||||
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,
|
||||
@ -0,0 +1,117 @@
|
||||
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);
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
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");
|
||||
@ -0,0 +1,30 @@
|
||||
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)
|
||||
@ -0,0 +1,76 @@
|
||||
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;
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
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
|
||||
@ -0,0 +1,70 @@
|
||||
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) {
|
||||
@ -0,0 +1,188 @@
|
||||
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
|
||||
@ -0,0 +1,48 @@
|
||||
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;
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
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
|
||||
@ -0,0 +1,250 @@
|
||||
From c45655386e532c85ff1d679fc2aa40b3aaff9916 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Fainelli <f.fainelli@gmail.com>
|
||||
Date: Sat, 14 Jun 2025 09:59:51 +0200
|
||||
Subject: [PATCH] net: dsa: b53: add support for FDB operations on 5325/5365
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
BCM5325 and BCM5365 are part of a much older generation of switches which,
|
||||
due to their limited number of ports and VLAN entries (up to 256) allowed
|
||||
a single 64-bit register to hold a full ARL entry.
|
||||
This requires a little bit of massaging when reading, writing and
|
||||
converting ARL entries in both directions.
|
||||
|
||||
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
|
||||
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
Link: https://patch.msgid.link/20250614080000.1884236-6-noltari@gmail.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/dsa/b53/b53_common.c | 101 +++++++++++++++++++++++++------
|
||||
drivers/net/dsa/b53/b53_priv.h | 29 +++++++++
|
||||
drivers/net/dsa/b53/b53_regs.h | 7 ++-
|
||||
3 files changed, 115 insertions(+), 22 deletions(-)
|
||||
|
||||
--- a/drivers/net/dsa/b53/b53_common.c
|
||||
+++ b/drivers/net/dsa/b53/b53_common.c
|
||||
@@ -1821,6 +1821,45 @@ static int b53_arl_read(struct b53_devic
|
||||
return *idx >= dev->num_arl_bins ? -ENOSPC : -ENOENT;
|
||||
}
|
||||
|
||||
+static int b53_arl_read_25(struct b53_device *dev, u64 mac,
|
||||
+ u16 vid, struct b53_arl_entry *ent, u8 *idx)
|
||||
+{
|
||||
+ DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES);
|
||||
+ unsigned int i;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = b53_arl_op_wait(dev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ bitmap_zero(free_bins, dev->num_arl_bins);
|
||||
+
|
||||
+ /* Read the bins */
|
||||
+ for (i = 0; i < dev->num_arl_bins; i++) {
|
||||
+ u64 mac_vid;
|
||||
+
|
||||
+ b53_read64(dev, B53_ARLIO_PAGE,
|
||||
+ B53_ARLTBL_MAC_VID_ENTRY(i), &mac_vid);
|
||||
+
|
||||
+ b53_arl_to_entry_25(ent, mac_vid);
|
||||
+
|
||||
+ if (!(mac_vid & ARLTBL_VALID_25)) {
|
||||
+ set_bit(i, free_bins);
|
||||
+ continue;
|
||||
+ }
|
||||
+ if ((mac_vid & ARLTBL_MAC_MASK) != mac)
|
||||
+ continue;
|
||||
+ if (dev->vlan_enabled &&
|
||||
+ ((mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25) != vid)
|
||||
+ continue;
|
||||
+ *idx = i;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ *idx = find_first_bit(free_bins, dev->num_arl_bins);
|
||||
+ return *idx >= dev->num_arl_bins ? -ENOSPC : -ENOENT;
|
||||
+}
|
||||
+
|
||||
static int b53_arl_op(struct b53_device *dev, int op, int port,
|
||||
const unsigned char *addr, u16 vid, bool is_valid)
|
||||
{
|
||||
@@ -1843,7 +1882,10 @@ static int b53_arl_op(struct b53_device
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = b53_arl_read(dev, mac, vid, &ent, &idx);
|
||||
+ if (is5325(dev) || is5365(dev))
|
||||
+ ret = b53_arl_read_25(dev, mac, vid, &ent, &idx);
|
||||
+ else
|
||||
+ ret = b53_arl_read(dev, mac, vid, &ent, &idx);
|
||||
|
||||
/* If this is a read, just finish now */
|
||||
if (op)
|
||||
@@ -1887,12 +1929,17 @@ static int b53_arl_op(struct b53_device
|
||||
ent.is_static = true;
|
||||
ent.is_age = false;
|
||||
memcpy(ent.mac, addr, ETH_ALEN);
|
||||
- b53_arl_from_entry(&mac_vid, &fwd_entry, &ent);
|
||||
+ if (is5325(dev) || is5365(dev))
|
||||
+ b53_arl_from_entry_25(&mac_vid, &ent);
|
||||
+ else
|
||||
+ b53_arl_from_entry(&mac_vid, &fwd_entry, &ent);
|
||||
|
||||
b53_write64(dev, B53_ARLIO_PAGE,
|
||||
B53_ARLTBL_MAC_VID_ENTRY(idx), mac_vid);
|
||||
- b53_write32(dev, B53_ARLIO_PAGE,
|
||||
- B53_ARLTBL_DATA_ENTRY(idx), fwd_entry);
|
||||
+
|
||||
+ if (!is5325(dev) && !is5365(dev))
|
||||
+ b53_write32(dev, B53_ARLIO_PAGE,
|
||||
+ B53_ARLTBL_DATA_ENTRY(idx), fwd_entry);
|
||||
|
||||
return b53_arl_rw_op(dev, 0);
|
||||
}
|
||||
@@ -1904,12 +1951,6 @@ int b53_fdb_add(struct dsa_switch *ds, i
|
||||
struct b53_device *priv = ds->priv;
|
||||
int ret;
|
||||
|
||||
- /* 5325 and 5365 require some more massaging, but could
|
||||
- * be supported eventually
|
||||
- */
|
||||
- if (is5325(priv) || is5365(priv))
|
||||
- return -EOPNOTSUPP;
|
||||
-
|
||||
mutex_lock(&priv->arl_mutex);
|
||||
ret = b53_arl_op(priv, 0, port, addr, vid, true);
|
||||
mutex_unlock(&priv->arl_mutex);
|
||||
@@ -1936,10 +1977,15 @@ EXPORT_SYMBOL(b53_fdb_del);
|
||||
static int b53_arl_search_wait(struct b53_device *dev)
|
||||
{
|
||||
unsigned int timeout = 1000;
|
||||
- u8 reg;
|
||||
+ u8 reg, offset;
|
||||
+
|
||||
+ if (is5325(dev) || is5365(dev))
|
||||
+ offset = B53_ARL_SRCH_CTL_25;
|
||||
+ else
|
||||
+ offset = B53_ARL_SRCH_CTL;
|
||||
|
||||
do {
|
||||
- b53_read8(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, ®);
|
||||
+ b53_read8(dev, B53_ARLIO_PAGE, offset, ®);
|
||||
if (!(reg & ARL_SRCH_STDN))
|
||||
return -ENOENT;
|
||||
|
||||
@@ -1956,13 +2002,24 @@ static void b53_arl_search_rd(struct b53
|
||||
struct b53_arl_entry *ent)
|
||||
{
|
||||
u64 mac_vid;
|
||||
- u32 fwd_entry;
|
||||
|
||||
- b53_read64(dev, B53_ARLIO_PAGE,
|
||||
- B53_ARL_SRCH_RSTL_MACVID(idx), &mac_vid);
|
||||
- b53_read32(dev, B53_ARLIO_PAGE,
|
||||
- B53_ARL_SRCH_RSTL(idx), &fwd_entry);
|
||||
- b53_arl_to_entry(ent, mac_vid, fwd_entry);
|
||||
+ if (is5325(dev)) {
|
||||
+ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_25,
|
||||
+ &mac_vid);
|
||||
+ b53_arl_to_entry_25(ent, mac_vid);
|
||||
+ } else if (is5365(dev)) {
|
||||
+ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_65,
|
||||
+ &mac_vid);
|
||||
+ b53_arl_to_entry_25(ent, mac_vid);
|
||||
+ } else {
|
||||
+ u32 fwd_entry;
|
||||
+
|
||||
+ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_MACVID(idx),
|
||||
+ &mac_vid);
|
||||
+ b53_read32(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL(idx),
|
||||
+ &fwd_entry);
|
||||
+ b53_arl_to_entry(ent, mac_vid, fwd_entry);
|
||||
+ }
|
||||
}
|
||||
|
||||
static int b53_fdb_copy(int port, const struct b53_arl_entry *ent,
|
||||
@@ -1986,14 +2043,20 @@ int b53_fdb_dump(struct dsa_switch *ds,
|
||||
struct b53_device *priv = ds->priv;
|
||||
struct b53_arl_entry results[2];
|
||||
unsigned int count = 0;
|
||||
+ u8 offset;
|
||||
int ret;
|
||||
u8 reg;
|
||||
|
||||
mutex_lock(&priv->arl_mutex);
|
||||
|
||||
+ if (is5325(priv) || is5365(priv))
|
||||
+ offset = B53_ARL_SRCH_CTL_25;
|
||||
+ else
|
||||
+ offset = B53_ARL_SRCH_CTL;
|
||||
+
|
||||
/* Start search operation */
|
||||
reg = ARL_SRCH_STDN;
|
||||
- b53_write8(priv, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, reg);
|
||||
+ b53_write8(priv, offset, B53_ARL_SRCH_CTL, reg);
|
||||
|
||||
do {
|
||||
ret = b53_arl_search_wait(priv);
|
||||
--- a/drivers/net/dsa/b53/b53_priv.h
|
||||
+++ b/drivers/net/dsa/b53/b53_priv.h
|
||||
@@ -317,6 +317,19 @@ static inline void b53_arl_to_entry(stru
|
||||
ent->vid = mac_vid >> ARLTBL_VID_S;
|
||||
}
|
||||
|
||||
+static inline void b53_arl_to_entry_25(struct b53_arl_entry *ent,
|
||||
+ u64 mac_vid)
|
||||
+{
|
||||
+ memset(ent, 0, sizeof(*ent));
|
||||
+ ent->port = (mac_vid >> ARLTBL_DATA_PORT_ID_S_25) &
|
||||
+ ARLTBL_DATA_PORT_ID_MASK_25;
|
||||
+ ent->is_valid = !!(mac_vid & ARLTBL_VALID_25);
|
||||
+ ent->is_age = !!(mac_vid & ARLTBL_AGE_25);
|
||||
+ ent->is_static = !!(mac_vid & ARLTBL_STATIC_25);
|
||||
+ u64_to_ether_addr(mac_vid, ent->mac);
|
||||
+ ent->vid = mac_vid >> ARLTBL_VID_S_65;
|
||||
+}
|
||||
+
|
||||
static inline void b53_arl_from_entry(u64 *mac_vid, u32 *fwd_entry,
|
||||
const struct b53_arl_entry *ent)
|
||||
{
|
||||
@@ -331,6 +344,22 @@ static inline void b53_arl_from_entry(u6
|
||||
*fwd_entry |= ARLTBL_AGE;
|
||||
}
|
||||
|
||||
+static inline void b53_arl_from_entry_25(u64 *mac_vid,
|
||||
+ const struct b53_arl_entry *ent)
|
||||
+{
|
||||
+ *mac_vid = ether_addr_to_u64(ent->mac);
|
||||
+ *mac_vid |= (u64)(ent->port & ARLTBL_DATA_PORT_ID_MASK_25) <<
|
||||
+ ARLTBL_DATA_PORT_ID_S_25;
|
||||
+ *mac_vid |= (u64)(ent->vid & ARLTBL_VID_MASK_25) <<
|
||||
+ ARLTBL_VID_S_65;
|
||||
+ if (ent->is_valid)
|
||||
+ *mac_vid |= ARLTBL_VALID_25;
|
||||
+ if (ent->is_static)
|
||||
+ *mac_vid |= ARLTBL_STATIC_25;
|
||||
+ if (ent->is_age)
|
||||
+ *mac_vid |= ARLTBL_AGE_25;
|
||||
+}
|
||||
+
|
||||
#ifdef CONFIG_BCM47XX
|
||||
|
||||
#include <linux/bcm47xx_nvram.h>
|
||||
--- a/drivers/net/dsa/b53/b53_regs.h
|
||||
+++ b/drivers/net/dsa/b53/b53_regs.h
|
||||
@@ -328,9 +328,10 @@
|
||||
#define ARLTBL_VID_MASK 0xfff
|
||||
#define ARLTBL_DATA_PORT_ID_S_25 48
|
||||
#define ARLTBL_DATA_PORT_ID_MASK_25 0xf
|
||||
-#define ARLTBL_AGE_25 BIT(61)
|
||||
-#define ARLTBL_STATIC_25 BIT(62)
|
||||
-#define ARLTBL_VALID_25 BIT(63)
|
||||
+#define ARLTBL_VID_S_65 53
|
||||
+#define ARLTBL_AGE_25 BIT_ULL(61)
|
||||
+#define ARLTBL_STATIC_25 BIT_ULL(62)
|
||||
+#define ARLTBL_VALID_25 BIT_ULL(63)
|
||||
|
||||
/* ARL Table Data Entry N Registers (32 bit) */
|
||||
#define B53_ARLTBL_DATA_ENTRY(n) ((0x10 * (n)) + 0x18)
|
||||
@ -0,0 +1,51 @@
|
||||
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);
|
||||
@ -0,0 +1,33 @@
|
||||
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)
|
||||
@ -0,0 +1,128 @@
|
||||
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
|
||||
*************************************************************************/
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
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,
|
||||
@ -0,0 +1,124 @@
|
||||
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)
|
||||
@ -0,0 +1,69 @@
|
||||
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;
|
||||
@ -0,0 +1,47 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
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;
|
||||
}
|
||||
@ -0,0 +1,100 @@
|
||||
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,
|
||||
@ -0,0 +1,33 @@
|
||||
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,
|
||||
@ -0,0 +1,77 @@
|
||||
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);
|
||||
@ -0,0 +1,72 @@
|
||||
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);
|
||||
@ -0,0 +1,77 @@
|
||||
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;
|
||||
@ -0,0 +1,61 @@
|
||||
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);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user