+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -20057,6 +20057,14 @@ T: git git://linuxtv.org/media_tree.git
+@@ -20058,6 +20058,14 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
F: drivers/media/i2c/imx415.c
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -20065,6 +20065,14 @@ T: git git://linuxtv.org/media_tree.git
+@@ -20066,6 +20066,14 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/imx477.yaml
F: drivers/media/i2c/imx477.c
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -20062,6 +20062,7 @@ M: Raspberry Pi Kernel Maintenance <kern
+@@ -20063,6 +20063,7 @@ M: Raspberry Pi Kernel Maintenance <kern
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -20106,6 +20106,14 @@ T: git git://linuxtv.org/media_tree.git
+@@ -20107,6 +20107,14 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/imx519.yaml
F: drivers/media/i2c/imx519.c
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -20111,7 +20111,7 @@ M: Raspberry Pi Kernel Maintenance <kern
+@@ -20112,7 +20112,7 @@ M: Raspberry Pi Kernel Maintenance <kern
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
+ };
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -20012,7 +20012,7 @@ M: Sakari Ailus <sakari.ailus@linux.inte
+@@ -20013,7 +20013,7 @@ M: Sakari Ailus <sakari.ailus@linux.inte
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
+...
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -18619,6 +18619,13 @@ S: Supported
+@@ -18620,6 +18620,13 @@ S: Supported
F: drivers/iio/light/rohm-bu27008.c
F: drivers/iio/light/rohm-bu27034.c
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -15871,6 +15871,14 @@ S: Maintained
+@@ -15872,6 +15872,14 @@ S: Maintained
T: git git://linuxtv.org/media_tree.git
F: drivers/media/i2c/ov5695.c
drivers cannot. If you rely on getting the inactive state, use .duty_cycle=0,
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -17437,7 +17437,7 @@ F: drivers/video/backlight/pwm_bl.c
+@@ -17438,7 +17438,7 @@ F: drivers/video/backlight/pwm_bl.c
F: include/dt-bindings/pwm/
F: include/linux/pwm.h
F: include/linux/pwm_backlight.h
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -18032,6 +18032,13 @@ L: linux-wireless@vger.kernel.org
+@@ -18033,6 +18033,13 @@ L: linux-wireless@vger.kernel.org
S: Orphan
F: drivers/net/wireless/legacy/ray*
+ };
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -18037,6 +18037,7 @@ M: Jacopo Mondi <jacopo.mondi@ideasonboa
+@@ -18038,6 +18038,7 @@ M: Jacopo Mondi <jacopo.mondi@ideasonboa
L: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
L: linux-media@vger.kernel.org
S: Maintained
+
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -18027,6 +18027,11 @@ F: drivers/ras/
+@@ -18028,6 +18028,11 @@ F: drivers/ras/
F: include/linux/ras.h
F: include/ras/ras_event.h
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -18031,6 +18031,7 @@ RASPBERRY PI RP2040 GPIO BRIDGE DRIVER
+@@ -18032,6 +18032,7 @@ RASPBERRY PI RP2040 GPIO BRIDGE DRIVER
M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
S: Maintained
F: Documentation/devicetree/bindings/spi/raspberrypi,rp2040-gpio-bridge.yaml
+
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -20127,6 +20127,13 @@ F: Documentation/devicetree/bindings/med
+@@ -20128,6 +20128,13 @@ F: Documentation/devicetree/bindings/med
F: Documentation/devicetree/bindings/media/i2c/imx477.yaml
F: drivers/media/i2c/imx477.c
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -20133,6 +20133,7 @@ L: linux-media@vger.kernel.org
+@@ -20134,6 +20134,7 @@ L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/sony,imx500.yaml
--- /dev/null
+From 1824520e7477bedf76bd08c32261c755e6405cd9 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 12 Aug 2024 02:56:41 +0100
+Subject: [PATCH] mtd: spinand: set bitflip_threshold to 75% of ECC strength
+
+Reporting an unclean read from SPI-NAND only when the maximum number
+of correctable bitflip errors has been hit seems a bit late.
+UBI LEB scrubbing, which depends on the lower MTD device reporting
+correctable bitflips, then only kicks in when it's almost too late.
+
+Set bitflip_threshold to 75% of the ECC strength, which is also the
+default for raw NAND.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/2117e387260b0a96f95b8e1652ff79e0e2d71d53.1723427450.git.daniel@makrotopia.org
+---
+ drivers/mtd/nand/spi/core.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -1287,6 +1287,7 @@ static int spinand_init(struct spinand_d
+ /* Propagate ECC information to mtd_info */
+ mtd->ecc_strength = nanddev_get_ecc_conf(nand)->strength;
+ mtd->ecc_step_size = nanddev_get_ecc_conf(nand)->step_size;
++ mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
+
+ ret = spinand_create_dirmaps(spinand);
+ if (ret) {
-From 446daf20b0a6790751459cdde0ff9fc8813e54d1 Mon Sep 17 00:00:00 2001
+From e2a9fcb36e851adb5b25c4acea53a290fd48a636 Mon Sep 17 00:00:00 2001
From: Robert Marko <robimarko@gmail.com>
-Date: Mon, 29 Jul 2024 14:09:16 +0200
+Date: Mon, 5 Aug 2024 19:51:02 +0200
Subject: [PATCH] mtd: spinand: winbond: add support for W25N01KV
Add support for Winbond W25N01KV 1Gbit SPI-NAND.
It has 4-bit on-die ECC.
Signed-off-by: Robert Marko <robimarko@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20240805175125.6658-1-robimarko@gmail.com
---
drivers/mtd/nand/spi/winbond.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
-@@ -76,6 +76,18 @@ static int w25m02gv_select_target(struct
+@@ -74,6 +74,18 @@ static int w25m02gv_select_target(struct
return spi_mem_exec_op(spinand->spimem, &op);
}
static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
-@@ -100,6 +112,11 @@ static int w25n02kv_ooblayout_free(struc
+@@ -98,6 +110,11 @@ static int w25n02kv_ooblayout_free(struc
return 0;
}
static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
.ecc = w25n02kv_ooblayout_ecc,
.free = w25n02kv_ooblayout_free,
-@@ -163,6 +180,15 @@ static const struct spinand_info winbond
+@@ -160,6 +177,15 @@ static const struct spinand_info winbond
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
--- /dev/null
+From 8928756d53d5b99dcd18073dc7738b8ebdbe7d96 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 2 May 2024 10:44:42 +0200
+Subject: [PATCH 1/6] net: move skb_gro_receive_list from udp to core
+
+This helper function will be used for TCP fraglist GRO support
+
+Acked-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ include/net/gro.h | 1 +
+ net/core/gro.c | 27 +++++++++++++++++++++++++++
+ net/ipv4/udp_offload.c | 27 ---------------------------
+ 3 files changed, 28 insertions(+), 27 deletions(-)
+
+--- a/include/net/gro.h
++++ b/include/net/gro.h
+@@ -439,6 +439,7 @@ static inline __wsum ip6_gro_compute_pse
+ }
+
+ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb);
++int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb);
+
+ /* Pass the currently batched GRO_NORMAL SKBs up to the stack. */
+ static inline void gro_normal_list(struct napi_struct *napi)
+--- a/net/core/gro.c
++++ b/net/core/gro.c
+@@ -228,6 +228,33 @@ done:
+ return 0;
+ }
+
++int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
++{
++ if (unlikely(p->len + skb->len >= 65536))
++ return -E2BIG;
++
++ if (NAPI_GRO_CB(p)->last == p)
++ skb_shinfo(p)->frag_list = skb;
++ else
++ NAPI_GRO_CB(p)->last->next = skb;
++
++ skb_pull(skb, skb_gro_offset(skb));
++
++ NAPI_GRO_CB(p)->last = skb;
++ NAPI_GRO_CB(p)->count++;
++ p->data_len += skb->len;
++
++ /* sk ownership - if any - completely transferred to the aggregated packet */
++ skb->destructor = NULL;
++ skb->sk = NULL;
++ p->truesize += skb->truesize;
++ p->len += skb->len;
++
++ NAPI_GRO_CB(skb)->same_flow = 1;
++
++ return 0;
++}
++
+
+ static void napi_gro_complete(struct napi_struct *napi, struct sk_buff *skb)
+ {
+--- a/net/ipv4/udp_offload.c
++++ b/net/ipv4/udp_offload.c
+@@ -474,33 +474,6 @@ out:
+ return segs;
+ }
+
+-static int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
+-{
+- if (unlikely(p->len + skb->len >= 65536))
+- return -E2BIG;
+-
+- if (NAPI_GRO_CB(p)->last == p)
+- skb_shinfo(p)->frag_list = skb;
+- else
+- NAPI_GRO_CB(p)->last->next = skb;
+-
+- skb_pull(skb, skb_gro_offset(skb));
+-
+- NAPI_GRO_CB(p)->last = skb;
+- NAPI_GRO_CB(p)->count++;
+- p->data_len += skb->len;
+-
+- /* sk ownership - if any - completely transferred to the aggregated packet */
+- skb->destructor = NULL;
+- skb->sk = NULL;
+- p->truesize += skb->truesize;
+- p->len += skb->len;
+-
+- NAPI_GRO_CB(skb)->same_flow = 1;
+-
+- return 0;
+-}
+-
+
+ #define UDP_GRO_CNT_MAX 64
+ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
--- /dev/null
+From bee88cd5bd83d40b8aec4d6cb729378f707f6197 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 2 May 2024 10:44:43 +0200
+Subject: [PATCH 2/6] net: add support for segmenting TCP fraglist GSO packets
+
+Preparation for adding TCP fraglist GRO support. It expects packets to be
+combined in a similar way as UDP fraglist GSO packets.
+For IPv4 packets, NAT is handled in the same way as UDP fraglist GSO.
+
+Acked-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ net/ipv4/tcp_offload.c | 67 ++++++++++++++++++++++++++++++++++++++++
+ net/ipv6/tcpv6_offload.c | 58 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 125 insertions(+)
+
+--- a/net/ipv4/tcp_offload.c
++++ b/net/ipv4/tcp_offload.c
+@@ -31,6 +31,70 @@ static void tcp_gso_tstamp(struct sk_buf
+ }
+ }
+
++static void __tcpv4_gso_segment_csum(struct sk_buff *seg,
++ __be32 *oldip, __be32 newip,
++ __be16 *oldport, __be16 newport)
++{
++ struct tcphdr *th;
++ struct iphdr *iph;
++
++ if (*oldip == newip && *oldport == newport)
++ return;
++
++ th = tcp_hdr(seg);
++ iph = ip_hdr(seg);
++
++ inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true);
++ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
++ *oldport = newport;
++
++ csum_replace4(&iph->check, *oldip, newip);
++ *oldip = newip;
++}
++
++static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs)
++{
++ const struct tcphdr *th;
++ const struct iphdr *iph;
++ struct sk_buff *seg;
++ struct tcphdr *th2;
++ struct iphdr *iph2;
++
++ seg = segs;
++ th = tcp_hdr(seg);
++ iph = ip_hdr(seg);
++ th2 = tcp_hdr(seg->next);
++ iph2 = ip_hdr(seg->next);
++
++ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
++ iph->daddr == iph2->daddr && iph->saddr == iph2->saddr)
++ return segs;
++
++ while ((seg = seg->next)) {
++ th2 = tcp_hdr(seg);
++ iph2 = ip_hdr(seg);
++
++ __tcpv4_gso_segment_csum(seg,
++ &iph2->saddr, iph->saddr,
++ &th2->source, th->source);
++ __tcpv4_gso_segment_csum(seg,
++ &iph2->daddr, iph->daddr,
++ &th2->dest, th->dest);
++ }
++
++ return segs;
++}
++
++static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb,
++ netdev_features_t features)
++{
++ skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
++ if (IS_ERR(skb))
++ return skb;
++
++ return __tcpv4_gso_segment_list_csum(skb);
++}
++
+ static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
+ netdev_features_t features)
+ {
+@@ -40,6 +104,9 @@ static struct sk_buff *tcp4_gso_segment(
+ if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
+ return ERR_PTR(-EINVAL);
+
++ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
++ return __tcp4_gso_segment_list(skb, features);
++
+ if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
+ const struct iphdr *iph = ip_hdr(skb);
+ struct tcphdr *th = tcp_hdr(skb);
+--- a/net/ipv6/tcpv6_offload.c
++++ b/net/ipv6/tcpv6_offload.c
+@@ -40,6 +40,61 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
+ return 0;
+ }
+
++static void __tcpv6_gso_segment_csum(struct sk_buff *seg,
++ __be16 *oldport, __be16 newport)
++{
++ struct tcphdr *th;
++
++ if (*oldport == newport)
++ return;
++
++ th = tcp_hdr(seg);
++ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
++ *oldport = newport;
++}
++
++static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs)
++{
++ const struct tcphdr *th;
++ const struct ipv6hdr *iph;
++ struct sk_buff *seg;
++ struct tcphdr *th2;
++ struct ipv6hdr *iph2;
++
++ seg = segs;
++ th = tcp_hdr(seg);
++ iph = ipv6_hdr(seg);
++ th2 = tcp_hdr(seg->next);
++ iph2 = ipv6_hdr(seg->next);
++
++ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
++ ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
++ ipv6_addr_equal(&iph->daddr, &iph2->daddr))
++ return segs;
++
++ while ((seg = seg->next)) {
++ th2 = tcp_hdr(seg);
++ iph2 = ipv6_hdr(seg);
++
++ iph2->saddr = iph->saddr;
++ iph2->daddr = iph->daddr;
++ __tcpv6_gso_segment_csum(seg, &th2->source, th->source);
++ __tcpv6_gso_segment_csum(seg, &th2->dest, th->dest);
++ }
++
++ return segs;
++}
++
++static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb,
++ netdev_features_t features)
++{
++ skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
++ if (IS_ERR(skb))
++ return skb;
++
++ return __tcpv6_gso_segment_list_csum(skb);
++}
++
+ static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
+ netdev_features_t features)
+ {
+@@ -51,6 +106,9 @@ static struct sk_buff *tcp6_gso_segment(
+ if (!pskb_may_pull(skb, sizeof(*th)))
+ return ERR_PTR(-EINVAL);
+
++ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
++ return __tcp6_gso_segment_list(skb, features);
++
+ if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
+ const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+ struct tcphdr *th = tcp_hdr(skb);
--- /dev/null
+From 8d95dc474f85481652a0e422d2f1f079de81f63c Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 2 May 2024 10:44:44 +0200
+Subject: [PATCH 3/6] net: add code for TCP fraglist GRO
+
+This implements fraglist GRO similar to how it's handled in UDP, however
+no functional changes are added yet. The next change adds a heuristic for
+using fraglist GRO instead of regular GRO.
+
+Acked-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ net/ipv4/tcp_offload.c | 21 +++++++++++++++++++++
+ net/ipv6/tcpv6_offload.c | 9 +++++++++
+ 2 files changed, 30 insertions(+)
+
+--- a/net/ipv4/tcp_offload.c
++++ b/net/ipv4/tcp_offload.c
+@@ -342,6 +342,18 @@ found:
+ flush |= p->decrypted ^ skb->decrypted;
+ #endif
+
++ if (unlikely(NAPI_GRO_CB(p)->is_flist)) {
++ flush |= (__force int)(flags ^ tcp_flag_word(th2));
++ flush |= skb->ip_summed != p->ip_summed;
++ flush |= skb->csum_level != p->csum_level;
++ flush |= NAPI_GRO_CB(p)->count >= 64;
++
++ if (flush || skb_gro_receive_list(p, skb))
++ mss = 1;
++
++ goto out_check_final;
++ }
++
+ if (flush || skb_gro_receive(p, skb)) {
+ mss = 1;
+ goto out_check_final;
+@@ -406,6 +418,15 @@ INDIRECT_CALLABLE_SCOPE int tcp4_gro_com
+ const struct iphdr *iph = ip_hdr(skb);
+ struct tcphdr *th = tcp_hdr(skb);
+
++ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) {
++ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV4;
++ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
++
++ __skb_incr_checksum_unnecessary(skb);
++
++ return 0;
++ }
++
+ th->check = ~tcp_v4_check(skb->len - thoff, iph->saddr,
+ iph->daddr, 0);
+ skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4;
+--- a/net/ipv6/tcpv6_offload.c
++++ b/net/ipv6/tcpv6_offload.c
+@@ -32,6 +32,15 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
+ const struct ipv6hdr *iph = ipv6_hdr(skb);
+ struct tcphdr *th = tcp_hdr(skb);
+
++ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) {
++ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV6;
++ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
++
++ __skb_incr_checksum_unnecessary(skb);
++
++ return 0;
++ }
++
+ th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr,
+ &iph->daddr, 0);
+ skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
--- /dev/null
+From 80e85fbdf19ecc4dfa31ecf639adb55555db02fe Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 2 May 2024 10:44:45 +0200
+Subject: [PATCH 4/6] net: create tcp_gro_lookup helper function
+
+This pulls the flow port matching out of tcp_gro_receive, so that it can be
+reused for the next change, which adds the TCP fraglist GRO heuristic.
+
+Acked-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ include/net/tcp.h | 1 +
+ net/ipv4/tcp_offload.c | 41 +++++++++++++++++++++++++----------------
+ 2 files changed, 26 insertions(+), 16 deletions(-)
+
+--- a/include/net/tcp.h
++++ b/include/net/tcp.h
+@@ -2101,6 +2101,7 @@ void tcp_v4_destroy_sock(struct sock *sk
+
+ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
+ netdev_features_t features);
++struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th);
+ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb);
+ INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *skb, int thoff));
+ INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb));
+--- a/net/ipv4/tcp_offload.c
++++ b/net/ipv4/tcp_offload.c
+@@ -251,6 +251,27 @@ out:
+ return segs;
+ }
+
++struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th)
++{
++ struct tcphdr *th2;
++ struct sk_buff *p;
++
++ list_for_each_entry(p, head, list) {
++ if (!NAPI_GRO_CB(p)->same_flow)
++ continue;
++
++ th2 = tcp_hdr(p);
++ if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
++ NAPI_GRO_CB(p)->same_flow = 0;
++ continue;
++ }
++
++ return p;
++ }
++
++ return NULL;
++}
++
+ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb)
+ {
+ struct sk_buff *pp = NULL;
+@@ -288,24 +309,12 @@ struct sk_buff *tcp_gro_receive(struct l
+ len = skb_gro_len(skb);
+ flags = tcp_flag_word(th);
+
+- list_for_each_entry(p, head, list) {
+- if (!NAPI_GRO_CB(p)->same_flow)
+- continue;
+-
+- th2 = tcp_hdr(p);
+-
+- if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
+- NAPI_GRO_CB(p)->same_flow = 0;
+- continue;
+- }
+-
+- goto found;
+- }
+- p = NULL;
+- goto out_check_final;
++ p = tcp_gro_lookup(head, th);
++ if (!p)
++ goto out_check_final;
+
+-found:
+ /* Include the IP ID check below from the inner most IP hdr */
++ th2 = tcp_hdr(p);
+ flush = NAPI_GRO_CB(p)->flush;
+ flush |= (__force int)(flags & TCP_FLAG_CWR);
+ flush |= (__force int)((flags ^ tcp_flag_word(th2)) &
--- /dev/null
+From 7516b27c555c1711ec17a5d891befb6986e573a3 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 2 May 2024 10:44:46 +0200
+Subject: [PATCH 5/6] net: create tcp_gro_header_pull helper function
+
+Pull the code out of tcp_gro_receive in order to access the tcp header
+from tcp4/6_gro_receive.
+
+Acked-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ include/net/tcp.h | 4 ++-
+ net/ipv4/tcp_offload.c | 55 +++++++++++++++++++++++++---------------
+ net/ipv6/tcpv6_offload.c | 18 +++++++++----
+ 3 files changed, 50 insertions(+), 27 deletions(-)
+
+--- a/include/net/tcp.h
++++ b/include/net/tcp.h
+@@ -2101,8 +2101,10 @@ void tcp_v4_destroy_sock(struct sock *sk
+
+ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
+ netdev_features_t features);
++struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb);
+ struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th);
+-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb);
++struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
++ struct tcphdr *th);
+ INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *skb, int thoff));
+ INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb));
+ INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *skb, int thoff));
+--- a/net/ipv4/tcp_offload.c
++++ b/net/ipv4/tcp_offload.c
+@@ -272,40 +272,46 @@ struct sk_buff *tcp_gro_lookup(struct li
+ return NULL;
+ }
+
+-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb)
++struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb)
+ {
+- struct sk_buff *pp = NULL;
+- struct sk_buff *p;
++ unsigned int thlen, hlen, off;
+ struct tcphdr *th;
+- struct tcphdr *th2;
+- unsigned int len;
+- unsigned int thlen;
+- __be32 flags;
+- unsigned int mss = 1;
+- unsigned int hlen;
+- unsigned int off;
+- int flush = 1;
+- int i;
+
+ off = skb_gro_offset(skb);
+ hlen = off + sizeof(*th);
+ th = skb_gro_header(skb, hlen, off);
+ if (unlikely(!th))
+- goto out;
++ return NULL;
+
+ thlen = th->doff * 4;
+ if (thlen < sizeof(*th))
+- goto out;
++ return NULL;
+
+ hlen = off + thlen;
+ if (skb_gro_header_hard(skb, hlen)) {
+ th = skb_gro_header_slow(skb, hlen, off);
+ if (unlikely(!th))
+- goto out;
++ return NULL;
+ }
+
+ skb_gro_pull(skb, thlen);
+
++ return th;
++}
++
++struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
++ struct tcphdr *th)
++{
++ unsigned int thlen = th->doff * 4;
++ struct sk_buff *pp = NULL;
++ struct sk_buff *p;
++ struct tcphdr *th2;
++ unsigned int len;
++ __be32 flags;
++ unsigned int mss = 1;
++ int flush = 1;
++ int i;
++
+ len = skb_gro_len(skb);
+ flags = tcp_flag_word(th);
+
+@@ -384,7 +390,6 @@ out_check_final:
+ if (p && (!NAPI_GRO_CB(skb)->same_flow || flush))
+ pp = p;
+
+-out:
+ NAPI_GRO_CB(skb)->flush |= (flush != 0);
+
+ return pp;
+@@ -411,15 +416,23 @@ EXPORT_SYMBOL(tcp_gro_complete);
+ INDIRECT_CALLABLE_SCOPE
+ struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
+ {
++ struct tcphdr *th;
++
+ /* Don't bother verifying checksum if we're going to flush anyway. */
+ if (!NAPI_GRO_CB(skb)->flush &&
+ skb_gro_checksum_validate(skb, IPPROTO_TCP,
+- inet_gro_compute_pseudo)) {
+- NAPI_GRO_CB(skb)->flush = 1;
+- return NULL;
+- }
++ inet_gro_compute_pseudo))
++ goto flush;
+
+- return tcp_gro_receive(head, skb);
++ th = tcp_gro_pull_header(skb);
++ if (!th)
++ goto flush;
++
++ return tcp_gro_receive(head, skb, th);
++
++flush:
++ NAPI_GRO_CB(skb)->flush = 1;
++ return NULL;
+ }
+
+ INDIRECT_CALLABLE_SCOPE int tcp4_gro_complete(struct sk_buff *skb, int thoff)
+--- a/net/ipv6/tcpv6_offload.c
++++ b/net/ipv6/tcpv6_offload.c
+@@ -16,15 +16,23 @@
+ INDIRECT_CALLABLE_SCOPE
+ struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
+ {
++ struct tcphdr *th;
++
+ /* Don't bother verifying checksum if we're going to flush anyway. */
+ if (!NAPI_GRO_CB(skb)->flush &&
+ skb_gro_checksum_validate(skb, IPPROTO_TCP,
+- ip6_gro_compute_pseudo)) {
+- NAPI_GRO_CB(skb)->flush = 1;
+- return NULL;
+- }
++ ip6_gro_compute_pseudo))
++ goto flush;
++
++ th = tcp_gro_pull_header(skb);
++ if (!th)
++ goto flush;
++
++ return tcp_gro_receive(head, skb, th);
+
+- return tcp_gro_receive(head, skb);
++flush:
++ NAPI_GRO_CB(skb)->flush = 1;
++ return NULL;
+ }
+
+ INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
--- /dev/null
+From c9d1d23e5239f41700be69133a5769ac5ebc88a8 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 2 May 2024 10:44:47 +0200
+Subject: [PATCH 6/6] net: add heuristic for enabling TCP fraglist GRO
+
+When forwarding TCP after GRO, software segmentation is very expensive,
+especially when the checksum needs to be recalculated.
+One case where that's currently unavoidable is when routing packets over
+PPPoE. Performance improves significantly when using fraglist GRO
+implemented in the same way as for UDP.
+
+When NETIF_F_GRO_FRAGLIST is enabled, perform a lookup for an established
+socket in the same netns as the receiving device. While this may not
+cover all relevant use cases in multi-netns configurations, it should be
+good enough for most configurations that need this.
+
+Here's a measurement of running 2 TCP streams through a MediaTek MT7622
+device (2-core Cortex-A53), which runs NAT with flow offload enabled from
+one ethernet port to PPPoE on another ethernet port + cake qdisc set to
+1Gbps.
+
+rx-gro-list off: 630 Mbit/s, CPU 35% idle
+rx-gro-list on: 770 Mbit/s, CPU 40% idle
+
+Acked-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Reviewed-by: David Ahern <dsahern@kernel.org>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ net/ipv4/tcp_offload.c | 32 ++++++++++++++++++++++++++++++++
+ net/ipv6/tcpv6_offload.c | 35 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 67 insertions(+)
+
+--- a/net/ipv4/tcp_offload.c
++++ b/net/ipv4/tcp_offload.c
+@@ -413,6 +413,36 @@ void tcp_gro_complete(struct sk_buff *sk
+ }
+ EXPORT_SYMBOL(tcp_gro_complete);
+
++static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
++ struct tcphdr *th)
++{
++ const struct iphdr *iph;
++ struct sk_buff *p;
++ struct sock *sk;
++ struct net *net;
++ int iif, sdif;
++
++ if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
++ return;
++
++ p = tcp_gro_lookup(head, th);
++ if (p) {
++ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
++ return;
++ }
++
++ inet_get_iif_sdif(skb, &iif, &sdif);
++ iph = skb_gro_network_header(skb);
++ net = dev_net(skb->dev);
++ sk = __inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
++ iph->saddr, th->source,
++ iph->daddr, ntohs(th->dest),
++ iif, sdif);
++ NAPI_GRO_CB(skb)->is_flist = !sk;
++ if (sk)
++ sock_put(sk);
++}
++
+ INDIRECT_CALLABLE_SCOPE
+ struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
+ {
+@@ -428,6 +458,8 @@ struct sk_buff *tcp4_gro_receive(struct
+ if (!th)
+ goto flush;
+
++ tcp4_check_fraglist_gro(head, skb, th);
++
+ return tcp_gro_receive(head, skb, th);
+
+ flush:
+--- a/net/ipv6/tcpv6_offload.c
++++ b/net/ipv6/tcpv6_offload.c
+@@ -7,12 +7,45 @@
+ */
+ #include <linux/indirect_call_wrapper.h>
+ #include <linux/skbuff.h>
++#include <net/inet6_hashtables.h>
+ #include <net/gro.h>
+ #include <net/protocol.h>
+ #include <net/tcp.h>
+ #include <net/ip6_checksum.h>
+ #include "ip6_offload.h"
+
++static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
++ struct tcphdr *th)
++{
++#if IS_ENABLED(CONFIG_IPV6)
++ const struct ipv6hdr *hdr;
++ struct sk_buff *p;
++ struct sock *sk;
++ struct net *net;
++ int iif, sdif;
++
++ if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
++ return;
++
++ p = tcp_gro_lookup(head, th);
++ if (p) {
++ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
++ return;
++ }
++
++ inet6_get_iif_sdif(skb, &iif, &sdif);
++ hdr = skb_gro_network_header(skb);
++ net = dev_net(skb->dev);
++ sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
++ &hdr->saddr, th->source,
++ &hdr->daddr, ntohs(th->dest),
++ iif, sdif);
++ NAPI_GRO_CB(skb)->is_flist = !sk;
++ if (sk)
++ sock_put(sk);
++#endif /* IS_ENABLED(CONFIG_IPV6) */
++}
++
+ INDIRECT_CALLABLE_SCOPE
+ struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
+ {
+@@ -28,6 +61,8 @@ struct sk_buff *tcp6_gro_receive(struct
+ if (!th)
+ goto flush;
+
++ tcp6_check_fraglist_gro(head, skb, th);
++
+ return tcp_gro_receive(head, skb, th);
+
+ flush:
+From 17bd3bd82f9f79f3feba15476c2b2c95a9b11ff8 Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 26 Sep 2024 10:41:30 +0200
+Date: Thu, 26 Sep 2024 10:53:14 +0200
Subject: [PATCH] net: gso: fix tcp fraglist segmentation after pull from
frag_list
Link: https://lore.kernel.org/netdev/20240922150450.3873767-1-willemdebruijn.kernel@gmail.com/
Fixes: bee88cd5bd83 ("net: add support for segmenting TCP fraglist GSO packets")
Cc: stable@vger.kernel.org
-Cc: Willem de Bruijn <willemb@google.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://patch.msgid.link/20240926085315.51524-1-nbd@nbd.name
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
+ net/ipv4/tcp_offload.c | 10 ++++++++--
+ net/ipv6/tcpv6_offload.c | 10 ++++++++--
+ 2 files changed, 16 insertions(+), 4 deletions(-)
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
--- /dev/null
+From daa624d3c2ddffdcbad140a9625a4064371db44f Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 11 Mar 2025 22:25:30 +0100
+Subject: [PATCH] net: ipv6: fix TCP GSO segmentation with NAT
+
+When updating the source/destination address, the TCP/UDP checksum needs to
+be updated as well.
+
+Fixes: bee88cd5bd83 ("net: add support for segmenting TCP fraglist GSO packets")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Link: https://patch.msgid.link/20250311212530.91519-1-nbd@nbd.name
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ net/ipv6/tcpv6_offload.c | 21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+--- a/net/ipv6/tcpv6_offload.c
++++ b/net/ipv6/tcpv6_offload.c
+@@ -93,14 +93,23 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
+ }
+
+ static void __tcpv6_gso_segment_csum(struct sk_buff *seg,
++ struct in6_addr *oldip,
++ const struct in6_addr *newip,
+ __be16 *oldport, __be16 newport)
+ {
+- struct tcphdr *th;
++ struct tcphdr *th = tcp_hdr(seg);
++
++ if (!ipv6_addr_equal(oldip, newip)) {
++ inet_proto_csum_replace16(&th->check, seg,
++ oldip->s6_addr32,
++ newip->s6_addr32,
++ true);
++ *oldip = *newip;
++ }
+
+ if (*oldport == newport)
+ return;
+
+- th = tcp_hdr(seg);
+ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
+ *oldport = newport;
+ }
+@@ -128,10 +137,10 @@ static struct sk_buff *__tcpv6_gso_segme
+ th2 = tcp_hdr(seg);
+ iph2 = ipv6_hdr(seg);
+
+- iph2->saddr = iph->saddr;
+- iph2->daddr = iph->daddr;
+- __tcpv6_gso_segment_csum(seg, &th2->source, th->source);
+- __tcpv6_gso_segment_csum(seg, &th2->dest, th->dest);
++ __tcpv6_gso_segment_csum(seg, &iph2->saddr, &iph->saddr,
++ &th2->source, th->source);
++ __tcpv6_gso_segment_csum(seg, &iph2->daddr, &iph->daddr,
++ &th2->dest, th->dest);
+ }
+
+ return segs;
+From 84443741faab9045d53f022a9ac6a6633067a481 Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 14 Feb 2024 15:24:41 +0100
+Date: Wed, 14 Feb 2024 15:42:35 +0100
Subject: [PATCH] netfilter: nf_tables: fix bidirectional offload regression
Commit 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules")
Cc: Vlad Buslov <vladbu@nvidia.com>
Fixes: 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules")
+Reported-by: Daniel Golle <daniel@makrotopia.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
+ net/netfilter/nft_flow_offload.c | 1 +
+ 1 file changed, 1 insertion(+)
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
-From 3b5a603bf66236b956287909556fd7ad4904450c Mon Sep 17 00:00:00 2001
+From cb77d0ad460e2c97a00c02ed78afdf45476e5e5f Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 24 Jan 2024 19:38:01 +0100
-Subject: [PATCH 3/3] arm64: dts: qcom: ipq8074: add clock-frequency to MDIO
- node
+Date: Wed, 31 Jan 2024 03:27:29 +0100
+Subject: [PATCH] arm64: dts: qcom: ipq8074: add clock-frequency to MDIO node
Add clock-frequency to MDIO node to set the MDC rate to 6.25Mhz instead
of using the default value of 390KHz from MDIO default divider.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
+Link: https://lore.kernel.org/r/20240131022731.2118-1-ansuelsmth@gmail.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 ++
1 file changed, 2 insertions(+)
+From 88806efc034a9830f483963326b99930ad519af1 Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 15 Oct 2024 10:13:55 +0200
+Date: Tue, 15 Oct 2024 10:17:55 +0200
Subject: [PATCH] net: ethernet: mtk_eth_soc: fix memory corruption during fq
dma init
Fixes: c57e55819443 ("net: ethernet: mtk_eth_soc: handle dma buffer size soc specific")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20241015081755.31060-1-nbd@nbd.name
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1182,7 +1182,7 @@ static int mtk_init_fq_dma(struct mtk_et
+@@ -1181,7 +1181,7 @@ static int mtk_init_fq_dma(struct mtk_et
if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
return -ENOMEM;
--- /dev/null
+From 637f41476384c76d3cd7dcf5947caf2c8b8d7a9b Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sat, 26 Oct 2024 14:52:25 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: fix path of MT7988 WO firmware
+
+linux-firmware commit 808cba84 ("mtk_wed: add firmware for mt7988
+Wireless Ethernet Dispatcher") added mt7988_wo_{0,1}.bin in the
+'mediatek/mt7988' directory while driver current expects the files in
+the 'mediatek' directory.
+
+Change path in the driver header now that the firmware has been added.
+
+Fixes: e2f64db13aa1 ("net: ethernet: mtk_wed: introduce WED support for MT7988")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://patch.msgid.link/Zxz0GWTR5X5LdWPe@pidgin.makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_wed_wo.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
+@@ -91,8 +91,8 @@ enum mtk_wed_dummy_cr_idx {
+ #define MT7981_FIRMWARE_WO "mediatek/mt7981_wo.bin"
+ #define MT7986_FIRMWARE_WO0 "mediatek/mt7986_wo_0.bin"
+ #define MT7986_FIRMWARE_WO1 "mediatek/mt7986_wo_1.bin"
+-#define MT7988_FIRMWARE_WO0 "mediatek/mt7988_wo_0.bin"
+-#define MT7988_FIRMWARE_WO1 "mediatek/mt7988_wo_1.bin"
++#define MT7988_FIRMWARE_WO0 "mediatek/mt7988/mt7988_wo_0.bin"
++#define MT7988_FIRMWARE_WO1 "mediatek/mt7988/mt7988_wo_1.bin"
+
+ #define MTK_WO_MCU_CFG_LS_BASE 0
+ #define MTK_WO_MCU_CFG_LS_HW_VER_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x000)
--- /dev/null
+From ac4aa9dbc702329c447d968325b055af84ae1b59 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 9 Apr 2024 03:24:12 +0100
+Subject: [PATCH] phy: add driver for MediaTek XFI T-PHY
+
+Add driver for MediaTek's XFI T-PHY which can be found in the MT7988
+SoC. The XFI T-PHY is a 10 Gigabit/s Ethernet SerDes PHY with muxes on
+the internal side to be used with either USXGMII PCS or LynxI PCS,
+depending on the selected PHY interface mode.
+
+The PHY can operates only in PHY_MODE_ETHERNET, the submode is one of
+PHY_INTERFACE_MODE_* corresponding to the supported modes:
+
+ * USXGMII \
+ * 10GBase-R }- USXGMII PCS - XGDM \
+ * 5GBase-R / \
+ }- Ethernet MAC
+ * 2500Base-X \ /
+ * 1000Base-X }- LynxI PCS - GDM /
+ * Cisco SGMII (MAC side) /
+
+I chose the name XFI T-PHY because names of functions dealing with the
+phy in the vendor driver are prefixed "xfi_pextp_".
+
+The register space used by the phy is called "pextp" in the vendor
+sources, which could be read as "_P_CI _ex_press _T_-_P_hy", and that
+is quite misleading as this phy isn't used for anything related to
+PCIe, so I wanted to find a better name.
+
+XFI is still somehow related (as in: you would find the relevant
+places using grep in the vendor driver when looking for that) and the
+term seemed to at least somehow be aligned with the function of that
+phy: Dealing with (up to) 10 Gbit/s Ethernet serialized differential
+signals.
+
+In order to work-around a performance issue present on the first of
+two XFI T-PHYs found in MT7988, special tuning is applied which can
+be selected by adding the 'mediatek,usxgmii-performance-errata'
+property to the device tree node, similar to how the vendor driver is
+doing that too.
+
+There is no documentation for most registers used for the
+analog/tuning part, however, most of the registers have been partially
+reverse-engineered from MediaTek's SDK implementation (see links, an
+opaque sequence of 32-bit register writes) and descriptions for all
+relevant digital registers and bits such as resets and muxes have been
+supplied by MediaTek.
+
+Link: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/b72d6cba92bf9e29fb035c03052fa1e86664a25b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
+Link: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/dec96a1d9b82cdcda4a56453fd0b453d4cab4b85/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://lore.kernel.org/r/8719c82634df7e8e984f1a608be3ba2f2d494fb4.1712625857.git.daniel@makrotopia.org
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ MAINTAINERS | 1 +
+ drivers/phy/mediatek/Kconfig | 11 +
+ drivers/phy/mediatek/Makefile | 1 +
+ drivers/phy/mediatek/phy-mtk-xfi-tphy.c | 451 ++++++++++++++++++++++++
+ 4 files changed, 464 insertions(+)
+ create mode 100644 drivers/phy/mediatek/phy-mtk-xfi-tphy.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -13366,6 +13366,7 @@ L: netdev@vger.kernel.org
+ S: Maintained
+ F: drivers/net/phy/mediatek-ge-soc.c
+ F: drivers/net/phy/mediatek-ge.c
++F: drivers/phy/mediatek/phy-mtk-xfi-tphy.c
+
+ MEDIATEK I2C CONTROLLER DRIVER
+ M: Qii Wang <qii.wang@mediatek.com>
+--- a/drivers/phy/mediatek/Kconfig
++++ b/drivers/phy/mediatek/Kconfig
+@@ -13,6 +13,17 @@ config PHY_MTK_PCIE
+ callback for PCIe GEN3 port, it supports software efuse
+ initialization.
+
++config PHY_MTK_XFI_TPHY
++ tristate "MediaTek 10GE SerDes XFI T-PHY driver"
++ depends on ARCH_MEDIATEK || COMPILE_TEST
++ depends on OF
++ select GENERIC_PHY
++ help
++ Say 'Y' here to add support for MediaTek XFI T-PHY driver.
++ The driver provides access to the Ethernet SerDes T-PHY supporting
++ 1GE and 2.5GE modes via the LynxI PCS, and 5GE and 10GE modes
++ via the USXGMII PCS found in MediaTek SoCs with 10G Ethernet.
++
+ config PHY_MTK_TPHY
+ tristate "MediaTek T-PHY Driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+--- a/drivers/phy/mediatek/Makefile
++++ b/drivers/phy/mediatek/Makefile
+@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-p
+ obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
+ obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o
+ obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o
++obj-$(CONFIG_PHY_MTK_XFI_TPHY) += phy-mtk-xfi-tphy.o
+
+ phy-mtk-hdmi-drv-y := phy-mtk-hdmi.o
+ phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt2701.o
+--- /dev/null
++++ b/drivers/phy/mediatek/phy-mtk-xfi-tphy.c
+@@ -0,0 +1,451 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * MediaTek 10GE SerDes XFI T-PHY driver
++ *
++ * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
++ * Bc-bocun Chen <bc-bocun.chen@mediatek.com>
++ * based on mtk_usxgmii.c and mtk_sgmii.c found in MediaTek's SDK (GPL-2.0)
++ * Copyright (c) 2022 MediaTek Inc.
++ * Author: Henry Yen <henry.yen@mediatek.com>
++ */
++
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/of.h>
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/phy.h>
++#include <linux/phy/phy.h>
++
++#include "phy-mtk-io.h"
++
++#define MTK_XFI_TPHY_NUM_CLOCKS 2
++
++#define REG_DIG_GLB_70 0x0070
++#define XTP_PCS_RX_EQ_IN_PROGRESS(x) FIELD_PREP(GENMASK(25, 24), (x))
++#define XTP_PCS_MODE_MASK GENMASK(17, 16)
++#define XTP_PCS_MODE(x) FIELD_PREP(GENMASK(17, 16), (x))
++#define XTP_PCS_RST_B BIT(15)
++#define XTP_FRC_PCS_RST_B BIT(14)
++#define XTP_PCS_PWD_SYNC_MASK GENMASK(13, 12)
++#define XTP_PCS_PWD_SYNC(x) FIELD_PREP(XTP_PCS_PWD_SYNC_MASK, (x))
++#define XTP_PCS_PWD_ASYNC_MASK GENMASK(11, 10)
++#define XTP_PCS_PWD_ASYNC(x) FIELD_PREP(XTP_PCS_PWD_ASYNC_MASK, (x))
++#define XTP_FRC_PCS_PWD_ASYNC BIT(8)
++#define XTP_PCS_UPDT BIT(4)
++#define XTP_PCS_IN_FR_RG BIT(0)
++
++#define REG_DIG_GLB_F4 0x00f4
++#define XFI_DPHY_PCS_SEL BIT(0)
++#define XFI_DPHY_PCS_SEL_SGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 1)
++#define XFI_DPHY_PCS_SEL_USXGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 0)
++#define XFI_DPHY_AD_SGDT_FRC_EN BIT(5)
++
++#define REG_DIG_LN_TRX_40 0x3040
++#define XTP_LN_FRC_TX_DATA_EN BIT(29)
++#define XTP_LN_TX_DATA_EN BIT(28)
++
++#define REG_DIG_LN_TRX_B0 0x30b0
++#define XTP_LN_FRC_TX_MACCK_EN BIT(5)
++#define XTP_LN_TX_MACCK_EN BIT(4)
++
++#define REG_ANA_GLB_D0 0x90d0
++#define XTP_GLB_USXGMII_SEL_MASK GENMASK(3, 1)
++#define XTP_GLB_USXGMII_SEL(x) FIELD_PREP(GENMASK(3, 1), (x))
++#define XTP_GLB_USXGMII_EN BIT(0)
++
++/**
++ * struct mtk_xfi_tphy - run-time data of the XFI phy instance
++ * @base: IO memory area to access phy registers.
++ * @dev: Kernel device used to output prefixed debug info.
++ * @reset: Reset control corresponding to the phy instance.
++ * @clocks: All clocks required for the phy to operate.
++ * @da_war: Enables work-around for 10GBase-R mode.
++ */
++struct mtk_xfi_tphy {
++ void __iomem *base;
++ struct device *dev;
++ struct reset_control *reset;
++ struct clk_bulk_data clocks[MTK_XFI_TPHY_NUM_CLOCKS];
++ bool da_war;
++};
++
++/**
++ * mtk_xfi_tphy_setup() - Setup phy for specified interface mode.
++ * @xfi_tphy: XFI phy instance.
++ * @interface: Ethernet interface mode
++ *
++ * The setup function is the condensed result of combining the 5 functions which
++ * setup the phy in MediaTek's GPL licensed public SDK sources. They can be found
++ * in mtk_sgmii.c[1] as well as mtk_usxgmii.c[2].
++ *
++ * Many magic values have been replaced by register and bit definitions, however,
++ * that has not been possible in all cases. While the vendor driver uses a
++ * sequence of 32-bit writes, here we try to only modify the actually required
++ * bits.
++ *
++ * [1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/b72d6cba92bf9e29fb035c03052fa1e86664a25b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
++ *
++ * [2]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/dec96a1d9b82cdcda4a56453fd0b453d4cab4b85/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++ */
++static void mtk_xfi_tphy_setup(struct mtk_xfi_tphy *xfi_tphy,
++ phy_interface_t interface)
++{
++ bool is_1g, is_2p5g, is_5g, is_10g, da_war, use_lynxi_pcs;
++
++ /* shorthands for specific clock speeds depending on interface mode */
++ is_1g = interface == PHY_INTERFACE_MODE_1000BASEX ||
++ interface == PHY_INTERFACE_MODE_SGMII;
++ is_2p5g = interface == PHY_INTERFACE_MODE_2500BASEX;
++ is_5g = interface == PHY_INTERFACE_MODE_5GBASER;
++ is_10g = interface == PHY_INTERFACE_MODE_10GBASER ||
++ interface == PHY_INTERFACE_MODE_USXGMII;
++
++ /* Is overriding 10GBase-R tuning value required? */
++ da_war = xfi_tphy->da_war && (interface == PHY_INTERFACE_MODE_10GBASER);
++
++ /* configure input mux to either
++ * - USXGMII PCS (64b/66b coding) for 5G/10G
++ * - LynxI PCS (8b/10b coding) for 1G/2.5G
++ */
++ use_lynxi_pcs = is_1g || is_2p5g;
++
++ dev_dbg(xfi_tphy->dev, "setting up for mode %s\n", phy_modes(interface));
++
++ /* Setup PLL setting */
++ mtk_phy_update_bits(xfi_tphy->base + 0x9024, 0x100000, is_10g ? 0x0 : 0x100000);
++ mtk_phy_update_bits(xfi_tphy->base + 0x2020, 0x202000, is_5g ? 0x202000 : 0x0);
++ mtk_phy_update_bits(xfi_tphy->base + 0x2030, 0x500, is_1g ? 0x0 : 0x500);
++ mtk_phy_update_bits(xfi_tphy->base + 0x2034, 0xa00, is_1g ? 0x0 : 0xa00);
++ mtk_phy_update_bits(xfi_tphy->base + 0x2040, 0x340000, is_1g ? 0x200000 : 0x140000);
++
++ /* Setup RXFE BW setting */
++ mtk_phy_update_bits(xfi_tphy->base + 0x50f0, 0xc10, is_1g ? 0x410 : is_5g ? 0x800 : 0x400);
++ mtk_phy_update_bits(xfi_tphy->base + 0x50e0, 0x4000, is_5g ? 0x0 : 0x4000);
++
++ /* Setup RX CDR setting */
++ mtk_phy_update_bits(xfi_tphy->base + 0x506c, 0x30000, is_5g ? 0x0 : 0x30000);
++ mtk_phy_update_bits(xfi_tphy->base + 0x5070, 0x670000, is_5g ? 0x620000 : 0x50000);
++ mtk_phy_update_bits(xfi_tphy->base + 0x5074, 0x180000, is_5g ? 0x180000 : 0x0);
++ mtk_phy_update_bits(xfi_tphy->base + 0x5078, 0xf000400, is_5g ? 0x8000000 :
++ 0x7000400);
++ mtk_phy_update_bits(xfi_tphy->base + 0x507c, 0x5000500, is_5g ? 0x4000400 :
++ 0x1000100);
++ mtk_phy_update_bits(xfi_tphy->base + 0x5080, 0x1410, is_1g ? 0x400 : is_5g ? 0x1010 : 0x0);
++ mtk_phy_update_bits(xfi_tphy->base + 0x5084, 0x30300, is_1g ? 0x30300 :
++ is_5g ? 0x30100 :
++ 0x100);
++ mtk_phy_update_bits(xfi_tphy->base + 0x5088, 0x60200, is_1g ? 0x20200 :
++ is_5g ? 0x40000 :
++ 0x20000);
++
++ /* Setting RXFE adaptation range setting */
++ mtk_phy_update_bits(xfi_tphy->base + 0x50e4, 0xc0000, is_5g ? 0x0 : 0xc0000);
++ mtk_phy_update_bits(xfi_tphy->base + 0x50e8, 0x40000, is_5g ? 0x0 : 0x40000);
++ mtk_phy_update_bits(xfi_tphy->base + 0x50ec, 0xa00, is_1g ? 0x200 : 0x800);
++ mtk_phy_update_bits(xfi_tphy->base + 0x50a8, 0xee0000, is_5g ? 0x800000 :
++ 0x6e0000);
++ mtk_phy_update_bits(xfi_tphy->base + 0x6004, 0x190000, is_5g ? 0x0 : 0x190000);
++
++ if (is_10g)
++ writel(0x01423342, xfi_tphy->base + 0x00f8);
++ else if (is_5g)
++ writel(0x00a132a1, xfi_tphy->base + 0x00f8);
++ else if (is_2p5g)
++ writel(0x009c329c, xfi_tphy->base + 0x00f8);
++ else
++ writel(0x00fa32fa, xfi_tphy->base + 0x00f8);
++
++ /* Force SGDT_OUT off and select PCS */
++ mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_F4,
++ XFI_DPHY_AD_SGDT_FRC_EN | XFI_DPHY_PCS_SEL,
++ XFI_DPHY_AD_SGDT_FRC_EN |
++ (use_lynxi_pcs ? XFI_DPHY_PCS_SEL_SGMII :
++ XFI_DPHY_PCS_SEL_USXGMII));
++
++ /* Force GLB_CKDET_OUT */
++ mtk_phy_set_bits(xfi_tphy->base + 0x0030, 0xc00);
++
++ /* Force AEQ on */
++ writel(XTP_PCS_RX_EQ_IN_PROGRESS(2) | XTP_PCS_PWD_SYNC(2) | XTP_PCS_PWD_ASYNC(2),
++ xfi_tphy->base + REG_DIG_GLB_70);
++
++ usleep_range(1, 5);
++ writel(XTP_LN_FRC_TX_DATA_EN, xfi_tphy->base + REG_DIG_LN_TRX_40);
++
++ /* Setup TX DA default value */
++ mtk_phy_update_bits(xfi_tphy->base + 0x30b0, 0x30, 0x20);
++ writel(0x00008a01, xfi_tphy->base + 0x3028);
++ writel(0x0000a884, xfi_tphy->base + 0x302c);
++ writel(0x00083002, xfi_tphy->base + 0x3024);
++
++ /* Setup RG default value */
++ if (use_lynxi_pcs) {
++ writel(0x00011110, xfi_tphy->base + 0x3010);
++ writel(0x40704000, xfi_tphy->base + 0x3048);
++ } else {
++ writel(0x00022220, xfi_tphy->base + 0x3010);
++ writel(0x0f020a01, xfi_tphy->base + 0x5064);
++ writel(0x06100600, xfi_tphy->base + 0x50b4);
++ if (interface == PHY_INTERFACE_MODE_USXGMII)
++ writel(0x40704000, xfi_tphy->base + 0x3048);
++ else
++ writel(0x47684100, xfi_tphy->base + 0x3048);
++ }
++
++ if (is_1g)
++ writel(0x0000c000, xfi_tphy->base + 0x3064);
++
++ /* Setup RX EQ initial value */
++ mtk_phy_update_bits(xfi_tphy->base + 0x3050, 0xa8000000,
++ (interface != PHY_INTERFACE_MODE_10GBASER) ? 0xa8000000 : 0x0);
++ mtk_phy_update_bits(xfi_tphy->base + 0x3054, 0xaa,
++ (interface != PHY_INTERFACE_MODE_10GBASER) ? 0xaa : 0x0);
++
++ if (!use_lynxi_pcs)
++ writel(0x00000f00, xfi_tphy->base + 0x306c);
++ else if (is_2p5g)
++ writel(0x22000f00, xfi_tphy->base + 0x306c);
++ else
++ writel(0x20200f00, xfi_tphy->base + 0x306c);
++
++ mtk_phy_update_bits(xfi_tphy->base + 0xa008, 0x10000, da_war ? 0x10000 : 0x0);
++
++ mtk_phy_update_bits(xfi_tphy->base + 0xa060, 0x50000, use_lynxi_pcs ? 0x50000 : 0x40000);
++
++ /* Setup PHYA speed */
++ mtk_phy_update_bits(xfi_tphy->base + REG_ANA_GLB_D0,
++ XTP_GLB_USXGMII_SEL_MASK | XTP_GLB_USXGMII_EN,
++ is_10g ? XTP_GLB_USXGMII_SEL(0) :
++ is_5g ? XTP_GLB_USXGMII_SEL(1) :
++ is_2p5g ? XTP_GLB_USXGMII_SEL(2) :
++ XTP_GLB_USXGMII_SEL(3));
++ mtk_phy_set_bits(xfi_tphy->base + REG_ANA_GLB_D0, XTP_GLB_USXGMII_EN);
++
++ /* Release reset */
++ mtk_phy_set_bits(xfi_tphy->base + REG_DIG_GLB_70,
++ XTP_PCS_RST_B | XTP_FRC_PCS_RST_B);
++ usleep_range(150, 500);
++
++ /* Switch to P0 */
++ mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_70,
++ XTP_PCS_IN_FR_RG |
++ XTP_FRC_PCS_PWD_ASYNC |
++ XTP_PCS_PWD_ASYNC_MASK |
++ XTP_PCS_PWD_SYNC_MASK |
++ XTP_PCS_UPDT,
++ XTP_PCS_IN_FR_RG |
++ XTP_FRC_PCS_PWD_ASYNC |
++ XTP_PCS_UPDT);
++ usleep_range(1, 5);
++
++ mtk_phy_clear_bits(xfi_tphy->base + REG_DIG_GLB_70, XTP_PCS_UPDT);
++ usleep_range(15, 50);
++
++ if (use_lynxi_pcs) {
++ /* Switch to Gen2 */
++ mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_70,
++ XTP_PCS_MODE_MASK | XTP_PCS_UPDT,
++ XTP_PCS_MODE(1) | XTP_PCS_UPDT);
++ } else {
++ /* Switch to Gen3 */
++ mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_70,
++ XTP_PCS_MODE_MASK | XTP_PCS_UPDT,
++ XTP_PCS_MODE(2) | XTP_PCS_UPDT);
++ }
++ usleep_range(1, 5);
++
++ mtk_phy_clear_bits(xfi_tphy->base + REG_DIG_GLB_70, XTP_PCS_UPDT);
++
++ usleep_range(100, 500);
++
++ /* Enable MAC CK */
++ mtk_phy_set_bits(xfi_tphy->base + REG_DIG_LN_TRX_B0, XTP_LN_TX_MACCK_EN);
++ mtk_phy_clear_bits(xfi_tphy->base + REG_DIG_GLB_F4, XFI_DPHY_AD_SGDT_FRC_EN);
++
++ /* Enable TX data */
++ mtk_phy_set_bits(xfi_tphy->base + REG_DIG_LN_TRX_40,
++ XTP_LN_FRC_TX_DATA_EN | XTP_LN_TX_DATA_EN);
++ usleep_range(400, 1000);
++}
++
++/**
++ * mtk_xfi_tphy_set_mode() - Setup phy for specified interface mode.
++ *
++ * @phy: Phy instance.
++ * @mode: Only PHY_MODE_ETHERNET is supported.
++ * @submode: An Ethernet interface mode.
++ *
++ * Validate selected mode and call function mtk_xfi_tphy_setup().
++ *
++ * Return:
++ * * %0 - OK
++ * * %-EINVAL - invalid mode
++ */
++static int mtk_xfi_tphy_set_mode(struct phy *phy, enum phy_mode mode, int
++ submode)
++{
++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
++
++ if (mode != PHY_MODE_ETHERNET)
++ return -EINVAL;
++
++ switch (submode) {
++ case PHY_INTERFACE_MODE_1000BASEX:
++ case PHY_INTERFACE_MODE_2500BASEX:
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_5GBASER:
++ case PHY_INTERFACE_MODE_10GBASER:
++ case PHY_INTERFACE_MODE_USXGMII:
++ mtk_xfi_tphy_setup(xfi_tphy, submode);
++ return 0;
++ default:
++ return -EINVAL;
++ }
++}
++
++/**
++ * mtk_xfi_tphy_reset() - Reset the phy.
++ *
++ * @phy: Phy instance.
++ *
++ * Reset the phy using the external reset controller.
++ *
++ * Return:
++ * %0 - OK
++ */
++static int mtk_xfi_tphy_reset(struct phy *phy)
++{
++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
++
++ reset_control_assert(xfi_tphy->reset);
++ usleep_range(100, 500);
++ reset_control_deassert(xfi_tphy->reset);
++ usleep_range(1, 10);
++
++ return 0;
++}
++
++/**
++ * mtk_xfi_tphy_power_on() - Power-on the phy.
++ *
++ * @phy: Phy instance.
++ *
++ * Prepare and enable all clocks required for the phy to operate.
++ *
++ * Return:
++ * See clk_bulk_prepare_enable().
++ */
++static int mtk_xfi_tphy_power_on(struct phy *phy)
++{
++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
++
++ return clk_bulk_prepare_enable(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
++}
++
++/**
++ * mtk_xfi_tphy_power_off() - Power-off the phy.
++ *
++ * @phy: Phy instance.
++ *
++ * Disable and unprepare all clocks previously enabled.
++ *
++ * Return:
++ * See clk_bulk_prepare_disable().
++ */
++static int mtk_xfi_tphy_power_off(struct phy *phy)
++{
++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
++
++ clk_bulk_disable_unprepare(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
++
++ return 0;
++}
++
++static const struct phy_ops mtk_xfi_tphy_ops = {
++ .power_on = mtk_xfi_tphy_power_on,
++ .power_off = mtk_xfi_tphy_power_off,
++ .set_mode = mtk_xfi_tphy_set_mode,
++ .reset = mtk_xfi_tphy_reset,
++ .owner = THIS_MODULE,
++};
++
++/**
++ * mtk_xfi_tphy_probe() - Probe phy instance from Device Tree.
++ * @pdev: Matching platform device.
++ *
++ * The probe function gets IO resource, clocks, reset controller and
++ * whether the DA work-around for 10GBase-R is required from Device Tree and
++ * allocates memory for holding that information in a struct mtk_xfi_tphy.
++ *
++ * Return:
++ * * %0 - OK
++ * * %-ENODEV - Missing associated Device Tree node (should never happen).
++ * * %-ENOMEM - Out of memory.
++ * * Any error value which devm_platform_ioremap_resource(),
++ * devm_clk_bulk_get(), devm_reset_control_get_exclusive(),
++ * devm_phy_create() or devm_of_phy_provider_register() may return.
++ */
++static int mtk_xfi_tphy_probe(struct platform_device *pdev)
++{
++ struct device_node *np = pdev->dev.of_node;
++ struct phy_provider *phy_provider;
++ struct mtk_xfi_tphy *xfi_tphy;
++ struct phy *phy;
++ int ret;
++
++ if (!np)
++ return -ENODEV;
++
++ xfi_tphy = devm_kzalloc(&pdev->dev, sizeof(*xfi_tphy), GFP_KERNEL);
++ if (!xfi_tphy)
++ return -ENOMEM;
++
++ xfi_tphy->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(xfi_tphy->base))
++ return PTR_ERR(xfi_tphy->base);
++
++ xfi_tphy->dev = &pdev->dev;
++ xfi_tphy->clocks[0].id = "topxtal";
++ xfi_tphy->clocks[1].id = "xfipll";
++ ret = devm_clk_bulk_get(&pdev->dev, MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
++ if (ret)
++ return ret;
++
++ xfi_tphy->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
++ if (IS_ERR(xfi_tphy->reset))
++ return PTR_ERR(xfi_tphy->reset);
++
++ xfi_tphy->da_war = of_property_read_bool(np, "mediatek,usxgmii-performance-errata");
++
++ phy = devm_phy_create(&pdev->dev, NULL, &mtk_xfi_tphy_ops);
++ if (IS_ERR(phy))
++ return PTR_ERR(phy);
++
++ phy_set_drvdata(phy, xfi_tphy);
++ phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
++
++ return PTR_ERR_OR_ZERO(phy_provider);
++}
++
++static const struct of_device_id mtk_xfi_tphy_match[] = {
++ { .compatible = "mediatek,mt7988-xfi-tphy", },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, mtk_xfi_tphy_match);
++
++static struct platform_driver mtk_xfi_tphy_driver = {
++ .probe = mtk_xfi_tphy_probe,
++ .driver = {
++ .name = "mtk-xfi-tphy",
++ .of_match_table = mtk_xfi_tphy_match,
++ },
++};
++module_platform_driver(mtk_xfi_tphy_driver);
++
++MODULE_DESCRIPTION("MediaTek 10GE SerDes XFI T-PHY driver");
++MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
++MODULE_AUTHOR("Bc-bocun Chen <bc-bocun.chen@mediatek.com>");
++MODULE_LICENSE("GPL");
-From d0562705bcd4cb9849156f095b2af0ec1bb53b56 Mon Sep 17 00:00:00 2001
+From 32596b101f6cd87ab1f6e6a1c2a44c70546dde48 Mon Sep 17 00:00:00 2001
From: Lech Perczak <lech.perczak@gmail.com>
-Date: Fri, 17 Nov 2023 21:33:04 +0100
+Date: Sat, 18 Nov 2023 00:23:52 +0100
Subject: [PATCH] ARM: dts: nxp: imx7d-pico: add cpu-supply nodes
The PICO-IMX7D SoM has the usual power supply configuration using
Cc: Fabio Estevam <festevam@gmail.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
-
Signed-off-by: Lech Perczak <lech.perczak@gmail.com>
+Reviewed-by: Fabio Estevam <festevam@gmail.com>
+Signed-off-by: Shawn Guo <shawnguo@kernel.org>
---
- arch/arm/boot/dts/imx7d-pico.dtsi | 8 ++++++++
+ arch/arm/boot/dts/nxp/imx/imx7d-pico.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/arch/arm/boot/dts/nxp/imx/imx7d-pico.dtsi
-From 38eb5b3370c29515d2ce92adac2d6eba96f276f5 Mon Sep 17 00:00:00 2001
+From 7d36c3573391dcf0da089298a4b5a25c39f7289d Mon Sep 17 00:00:00 2001
From: INAGAKI Hiroshi <musashino.open@gmail.com>
-Date: Wed, 20 Mar 2024 15:32:18 +0900
-Subject: [PATCH v2 1/2] dt-bindings: leds: add LED_FUNCTION_MOBILE for mobile
- network
+Date: Sat, 23 Mar 2024 16:36:09 +0900
+Subject: [PATCH] dt-bindings: leds: Add LED_FUNCTION_MOBILE for mobile network
Add LED_FUNCTION_MOBILE for LEDs that indicate status of mobile network
connection. This is useful to distinguish those LEDs from LEDs that
Suggested-by: Hauke Mehrtens <hauke@hauke-m.de>
Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Link: https://lore.kernel.org/r/20240323074326.1428-2-musashino.open@gmail.com
+Signed-off-by: Lee Jones <lee@kernel.org>
---
include/dt-bindings/leds/common.h | 1 +
1 file changed, 1 insertion(+)
-From e22afe910afcfb51b6ba6a0ae776939959727f54 Mon Sep 17 00:00:00 2001
+From 77b9f2d6fd9bf34ec810de6bdad42d7d0a47d31b Mon Sep 17 00:00:00 2001
From: INAGAKI Hiroshi <musashino.open@gmail.com>
-Date: Wed, 20 Mar 2024 15:59:06 +0900
-Subject: [PATCH v2 2/2] dt-bindings: leds: add LED_FUNCTION_SPEED_* for link
- speed on LAN/WAN
+Date: Sat, 23 Mar 2024 16:36:10 +0900
+Subject: [PATCH] dt-bindings: leds: Add LED_FUNCTION_SPEED_* for link speed on
+ LAN/WAN
Add LED_FUNCTION_SPEED_LAN and LED_FUNCTION_SPEED_WAN for LEDs that
indicate link speed of ethernet ports on LAN/WAN. This is useful to
- 100 Mbps: amber:speed-(lan|wan)-N
- 10 Mbps: (none, turned off)
-Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Link: https://lore.kernel.org/r/20240323074326.1428-3-musashino.open@gmail.com
+Signed-off-by: Lee Jones <lee@kernel.org>
---
include/dt-bindings/leds/common.h | 2 ++
1 file changed, 2 insertions(+)
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -21988,6 +21988,7 @@ U-BOOT ENVIRONMENT VARIABLES
+@@ -21989,6 +21989,7 @@ U-BOOT ENVIRONMENT VARIABLES
M: Rafał Miłecki <rafal@milecki.pl>
S: Maintained
F: Documentation/devicetree/bindings/nvmem/u-boot,env.yaml
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
-@@ -169,6 +169,51 @@ static const struct spinand_info winbond
+@@ -195,6 +195,51 @@ static const struct spinand_info winbond
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
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),
-@@ -118,6 +120,7 @@ static int w25n02kv_ecc_get_status(struc
+@@ -135,6 +137,7 @@ static int w25n02kv_ecc_get_status(struc
return -EBADMSG;
case STATUS_ECC_HAS_BITFLIPS:
/*
* Let's try to retrieve the real maximum number of bitflips
* in order to avoid forcing the wear-leveling layer to move
-@@ -214,6 +217,15 @@ static const struct spinand_info winbond
+@@ -240,6 +243,15 @@ static const struct spinand_info winbond
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25m02gv_ooblayout, w25n02kv_ecc_get_status)),
-From a7a94ca21ac0f347f683d33c72b4aab57ce5eec3 Mon Sep 17 00:00:00 2001
+From 9da39ef332c417ce52732564c1c682a6e1209302 Mon Sep 17 00:00:00 2001
From: Florian Eckert <fe@dev.tdt.de>
-Date: Mon, 20 Nov 2023 11:13:20 +0100
+Date: Mon, 4 Dec 2023 15:13:35 +0100
Subject: [PATCH] tools/thermal/tmon: Fix compilation warning for wrong format
The following warnings are shown during compilation:
To fix this, the correct string format must be used for printing.
Signed-off-by: Florian Eckert <fe@dev.tdt.de>
+Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
+Link: https://lore.kernel.org/r/20231204141335.2798194-1-fe@dev.tdt.de
---
tools/thermal/tmon/tui.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
+++ /dev/null
-From patchwork Mon Aug 12 01:56:41 2024
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 1971406
-Return-Path:
- <linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org>
-X-Original-To: incoming@patchwork.ozlabs.org
-Delivered-To: patchwork-incoming@legolas.ozlabs.org
-Date: Mon, 12 Aug 2024 02:56:41 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: Miquel Raynal <miquel.raynal@bootlin.com>,
- Richard Weinberger <richard@nod.at>,
- Vignesh Raghavendra <vigneshr@ti.com>,
- Tudor Ambarus <tudor.ambarus@linaro.org>,
- Daniel Golle <daniel@makrotopia.org>,
- Mika Westerberg <mika.westerberg@linux.intel.com>,
- Chia-Lin Kao <acelan.kao@canonical.com>,
- Martin Kurbanov <mmkurbanov@salutedevices.com>,
- linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org
-Subject: [PATCH] mtd: spinand: set bitflip_threshold to 75% of ECC strength
-Message-ID:
- <2117e387260b0a96f95b8e1652ff79e0e2d71d53.1723427450.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-X-BeenThere: linux-mtd@lists.infradead.org
-X-Mailman-Version: 2.1.34
-Precedence: list
-List-Id: Linux MTD discussion mailing list <linux-mtd.lists.infradead.org>
-List-Unsubscribe: <http://lists.infradead.org/mailman/options/linux-mtd>,
- <mailto:linux-mtd-request@lists.infradead.org?subject=unsubscribe>
-List-Archive: <http://lists.infradead.org/pipermail/linux-mtd/>
-List-Post: <mailto:linux-mtd@lists.infradead.org>
-List-Help: <mailto:linux-mtd-request@lists.infradead.org?subject=help>
-List-Subscribe: <http://lists.infradead.org/mailman/listinfo/linux-mtd>,
- <mailto:linux-mtd-request@lists.infradead.org?subject=subscribe>
-Sender: "linux-mtd" <linux-mtd-bounces@lists.infradead.org>
-Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org
-
-Reporting an unclean read from SPI-NAND only when the maximum number
-of correctable bitflip errors has been hit seems a bit late.
-UBI LEB scrubbing, which depends on the lower MTD device reporting
-correctable bitflips, then only kicks in when it's almost too late.
-
-Set bitflip_threshold to 75% of the ECC strength, which is also the
-default for raw NAND.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
----
- drivers/mtd/nand/spi/core.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/mtd/nand/spi/core.c
-+++ b/drivers/mtd/nand/spi/core.c
-@@ -1287,6 +1287,7 @@ static int spinand_init(struct spinand_d
- /* Propagate ECC information to mtd_info */
- mtd->ecc_strength = nanddev_get_ecc_conf(nand)->strength;
- mtd->ecc_step_size = nanddev_get_ecc_conf(nand)->step_size;
-+ mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
-
- ret = spinand_create_dirmaps(spinand);
- if (ret) {
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -22015,6 +22015,12 @@ F: Documentation/filesystems/ubifs-authe
+@@ -22016,6 +22016,12 @@ F: Documentation/filesystems/ubifs-authe
F: Documentation/filesystems/ubifs.rst
F: fs/ubifs/
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 23 Apr 2024 11:23:03 +0200
-Subject: [PATCH] net: add TCP fraglist GRO support
-
-When forwarding TCP after GRO, software segmentation is very expensive,
-especially when the checksum needs to be recalculated.
-One case where that's currently unavoidable is when routing packets over
-PPPoE. Performance improves significantly when using fraglist GRO
-implemented in the same way as for UDP.
-
-Here's a measurement of running 2 TCP streams through a MediaTek MT7622
-device (2-core Cortex-A53), which runs NAT with flow offload enabled from
-one ethernet port to PPPoE on another ethernet port + cake qdisc set to
-1Gbps.
-
-rx-gro-list off: 630 Mbit/s, CPU 35% idle
-rx-gro-list on: 770 Mbit/s, CPU 40% idle
-
-Signe-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/gro.h
-+++ b/include/net/gro.h
-@@ -439,6 +439,7 @@ static inline __wsum ip6_gro_compute_pse
- }
-
- int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb);
-+int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb);
-
- /* Pass the currently batched GRO_NORMAL SKBs up to the stack. */
- static inline void gro_normal_list(struct napi_struct *napi)
---- a/include/net/tcp.h
-+++ b/include/net/tcp.h
-@@ -2101,7 +2101,10 @@ void tcp_v4_destroy_sock(struct sock *sk
-
- struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
- netdev_features_t features);
--struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb);
-+struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb);
-+struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th);
-+struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
-+ struct tcphdr *th);
- INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *skb, int thoff));
- INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb));
- INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *skb, int thoff));
---- a/net/core/gro.c
-+++ b/net/core/gro.c
-@@ -228,6 +228,33 @@ done:
- return 0;
- }
-
-+int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
-+{
-+ if (unlikely(p->len + skb->len >= 65536))
-+ return -E2BIG;
-+
-+ if (NAPI_GRO_CB(p)->last == p)
-+ skb_shinfo(p)->frag_list = skb;
-+ else
-+ NAPI_GRO_CB(p)->last->next = skb;
-+
-+ skb_pull(skb, skb_gro_offset(skb));
-+
-+ NAPI_GRO_CB(p)->last = skb;
-+ NAPI_GRO_CB(p)->count++;
-+ p->data_len += skb->len;
-+
-+ /* sk ownership - if any - completely transferred to the aggregated packet */
-+ skb->destructor = NULL;
-+ skb->sk = NULL;
-+ p->truesize += skb->truesize;
-+ p->len += skb->len;
-+
-+ NAPI_GRO_CB(skb)->same_flow = 1;
-+
-+ return 0;
-+}
-+
-
- static void napi_gro_complete(struct napi_struct *napi, struct sk_buff *skb)
- {
---- a/net/ipv4/tcp_offload.c
-+++ b/net/ipv4/tcp_offload.c
-@@ -31,6 +31,70 @@ static void tcp_gso_tstamp(struct sk_buf
- }
- }
-
-+static void __tcpv4_gso_segment_csum(struct sk_buff *seg,
-+ __be32 *oldip, __be32 newip,
-+ __be16 *oldport, __be16 newport)
-+{
-+ struct tcphdr *th;
-+ struct iphdr *iph;
-+
-+ if (*oldip == newip && *oldport == newport)
-+ return;
-+
-+ th = tcp_hdr(seg);
-+ iph = ip_hdr(seg);
-+
-+ inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true);
-+ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
-+ *oldport = newport;
-+
-+ csum_replace4(&iph->check, *oldip, newip);
-+ *oldip = newip;
-+}
-+
-+static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs)
-+{
-+ const struct tcphdr *th;
-+ const struct iphdr *iph;
-+ struct sk_buff *seg;
-+ struct tcphdr *th2;
-+ struct iphdr *iph2;
-+
-+ seg = segs;
-+ th = tcp_hdr(seg);
-+ iph = ip_hdr(seg);
-+ th2 = tcp_hdr(seg->next);
-+ iph2 = ip_hdr(seg->next);
-+
-+ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
-+ iph->daddr == iph2->daddr && iph->saddr == iph2->saddr)
-+ return segs;
-+
-+ while ((seg = seg->next)) {
-+ th2 = tcp_hdr(seg);
-+ iph2 = ip_hdr(seg);
-+
-+ __tcpv4_gso_segment_csum(seg,
-+ &iph2->saddr, iph->saddr,
-+ &th2->source, th->source);
-+ __tcpv4_gso_segment_csum(seg,
-+ &iph2->daddr, iph->daddr,
-+ &th2->dest, th->dest);
-+ }
-+
-+ return segs;
-+}
-+
-+static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb,
-+ netdev_features_t features)
-+{
-+ skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
-+ if (IS_ERR(skb))
-+ return skb;
-+
-+ return __tcpv4_gso_segment_list_csum(skb);
-+}
-+
- static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
- netdev_features_t features)
- {
-@@ -40,6 +104,9 @@ static struct sk_buff *tcp4_gso_segment(
- if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
- return ERR_PTR(-EINVAL);
-
-+ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
-+ return __tcp4_gso_segment_list(skb, features);
-+
- if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
- const struct iphdr *iph = ip_hdr(skb);
- struct tcphdr *th = tcp_hdr(skb);
-@@ -184,61 +251,76 @@ out:
- return segs;
- }
-
--struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb)
-+struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th)
- {
-- struct sk_buff *pp = NULL;
-+ struct tcphdr *th2;
- struct sk_buff *p;
-+
-+ list_for_each_entry(p, head, list) {
-+ if (!NAPI_GRO_CB(p)->same_flow)
-+ continue;
-+
-+ th2 = tcp_hdr(p);
-+ if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
-+ NAPI_GRO_CB(p)->same_flow = 0;
-+ continue;
-+ }
-+
-+ return p;
-+ }
-+
-+ return NULL;
-+}
-+
-+struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb)
-+{
-+ unsigned int thlen, hlen, off;
- struct tcphdr *th;
-- struct tcphdr *th2;
-- unsigned int len;
-- unsigned int thlen;
-- __be32 flags;
-- unsigned int mss = 1;
-- unsigned int hlen;
-- unsigned int off;
-- int flush = 1;
-- int i;
-
- off = skb_gro_offset(skb);
- hlen = off + sizeof(*th);
- th = skb_gro_header(skb, hlen, off);
- if (unlikely(!th))
-- goto out;
-+ return NULL;
-
- thlen = th->doff * 4;
- if (thlen < sizeof(*th))
-- goto out;
-+ return NULL;
-
- hlen = off + thlen;
- if (skb_gro_header_hard(skb, hlen)) {
- th = skb_gro_header_slow(skb, hlen, off);
- if (unlikely(!th))
-- goto out;
-+ return NULL;
- }
-
- skb_gro_pull(skb, thlen);
-
-- len = skb_gro_len(skb);
-- flags = tcp_flag_word(th);
--
-- list_for_each_entry(p, head, list) {
-- if (!NAPI_GRO_CB(p)->same_flow)
-- continue;
-+ return th;
-+}
-
-- th2 = tcp_hdr(p);
-+struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
-+ struct tcphdr *th)
-+{
-+ unsigned int thlen = th->doff * 4;
-+ struct sk_buff *pp = NULL;
-+ struct sk_buff *p;
-+ struct tcphdr *th2;
-+ unsigned int len;
-+ __be32 flags;
-+ unsigned int mss = 1;
-+ int flush = 1;
-+ int i;
-
-- if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
-- NAPI_GRO_CB(p)->same_flow = 0;
-- continue;
-- }
-+ len = skb_gro_len(skb);
-+ flags = tcp_flag_word(th);
-
-- goto found;
-- }
-- p = NULL;
-- goto out_check_final;
-+ p = tcp_gro_lookup(head, th);
-+ if (!p)
-+ goto out_check_final;
-
--found:
- /* Include the IP ID check below from the inner most IP hdr */
-+ th2 = tcp_hdr(p);
- flush = NAPI_GRO_CB(p)->flush;
- flush |= (__force int)(flags & TCP_FLAG_CWR);
- flush |= (__force int)((flags ^ tcp_flag_word(th2)) &
-@@ -275,6 +357,19 @@ found:
- flush |= p->decrypted ^ skb->decrypted;
- #endif
-
-+ if (unlikely(NAPI_GRO_CB(p)->is_flist)) {
-+ flush |= (__force int)(flags ^ tcp_flag_word(th2));
-+ flush |= skb->ip_summed != p->ip_summed;
-+ flush |= skb->csum_level != p->csum_level;
-+ flush |= !pskb_may_pull(skb, skb_gro_offset(skb));
-+ flush |= NAPI_GRO_CB(p)->count >= 64;
-+
-+ if (flush || skb_gro_receive_list(p, skb))
-+ mss = 1;
-+
-+ goto out_check_final;
-+ }
-+
- if (flush || skb_gro_receive(p, skb)) {
- mss = 1;
- goto out_check_final;
-@@ -296,7 +391,6 @@ out_check_final:
- if (p && (!NAPI_GRO_CB(skb)->same_flow || flush))
- pp = p;
-
--out:
- NAPI_GRO_CB(skb)->flush |= (flush != 0);
-
- return pp;
-@@ -320,18 +414,58 @@ void tcp_gro_complete(struct sk_buff *sk
- }
- EXPORT_SYMBOL(tcp_gro_complete);
-
-+static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
-+ struct tcphdr *th)
-+{
-+ const struct iphdr *iph;
-+ struct sk_buff *p;
-+ struct sock *sk;
-+ struct net *net;
-+ int iif, sdif;
-+
-+ if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST))
-+ return;
-+
-+ p = tcp_gro_lookup(head, th);
-+ if (p) {
-+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
-+ return;
-+ }
-+
-+ inet_get_iif_sdif(skb, &iif, &sdif);
-+ iph = skb_gro_network_header(skb);
-+ net = dev_net(skb->dev);
-+ sk = __inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
-+ iph->saddr, th->source,
-+ iph->daddr, ntohs(th->dest),
-+ iif, sdif);
-+ NAPI_GRO_CB(skb)->is_flist = !sk;
-+ if (sk)
-+ sock_put(sk);
-+}
-+
- INDIRECT_CALLABLE_SCOPE
- struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
- {
-+ struct tcphdr *th;
-+
- /* Don't bother verifying checksum if we're going to flush anyway. */
- if (!NAPI_GRO_CB(skb)->flush &&
- skb_gro_checksum_validate(skb, IPPROTO_TCP,
-- inet_gro_compute_pseudo)) {
-- NAPI_GRO_CB(skb)->flush = 1;
-- return NULL;
-- }
-+ inet_gro_compute_pseudo))
-+ goto flush;
-+
-+ th = tcp_gro_pull_header(skb);
-+ if (!th)
-+ goto flush;
-
-- return tcp_gro_receive(head, skb);
-+ tcp4_check_fraglist_gro(head, skb, th);
-+
-+ return tcp_gro_receive(head, skb, th);
-+
-+flush:
-+ NAPI_GRO_CB(skb)->flush = 1;
-+ return NULL;
- }
-
- INDIRECT_CALLABLE_SCOPE int tcp4_gro_complete(struct sk_buff *skb, int thoff)
-@@ -339,6 +473,15 @@ INDIRECT_CALLABLE_SCOPE int tcp4_gro_com
- const struct iphdr *iph = ip_hdr(skb);
- struct tcphdr *th = tcp_hdr(skb);
-
-+ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) {
-+ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV4;
-+ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
-+
-+ __skb_incr_checksum_unnecessary(skb);
-+
-+ return 0;
-+ }
-+
- th->check = ~tcp_v4_check(skb->len - thoff, iph->saddr,
- iph->daddr, 0);
- skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4;
---- a/net/ipv4/udp_offload.c
-+++ b/net/ipv4/udp_offload.c
-@@ -474,33 +474,6 @@ out:
- return segs;
- }
-
--static int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
--{
-- if (unlikely(p->len + skb->len >= 65536))
-- return -E2BIG;
--
-- if (NAPI_GRO_CB(p)->last == p)
-- skb_shinfo(p)->frag_list = skb;
-- else
-- NAPI_GRO_CB(p)->last->next = skb;
--
-- skb_pull(skb, skb_gro_offset(skb));
--
-- NAPI_GRO_CB(p)->last = skb;
-- NAPI_GRO_CB(p)->count++;
-- p->data_len += skb->len;
--
-- /* sk ownership - if any - completely transferred to the aggregated packet */
-- skb->destructor = NULL;
-- skb->sk = NULL;
-- p->truesize += skb->truesize;
-- p->len += skb->len;
--
-- NAPI_GRO_CB(skb)->same_flow = 1;
--
-- return 0;
--}
--
-
- #define UDP_GRO_CNT_MAX 64
- static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
---- a/net/ipv6/tcpv6_offload.c
-+++ b/net/ipv6/tcpv6_offload.c
-@@ -7,24 +7,67 @@
- */
- #include <linux/indirect_call_wrapper.h>
- #include <linux/skbuff.h>
-+#include <net/inet6_hashtables.h>
- #include <net/gro.h>
- #include <net/protocol.h>
- #include <net/tcp.h>
- #include <net/ip6_checksum.h>
- #include "ip6_offload.h"
-
-+static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
-+ struct tcphdr *th)
-+{
-+#if IS_ENABLED(CONFIG_IPV6)
-+ const struct ipv6hdr *hdr;
-+ struct sk_buff *p;
-+ struct sock *sk;
-+ struct net *net;
-+ int iif, sdif;
-+
-+ if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST))
-+ return;
-+
-+ p = tcp_gro_lookup(head, th);
-+ if (p) {
-+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
-+ return;
-+ }
-+
-+ inet6_get_iif_sdif(skb, &iif, &sdif);
-+ hdr = skb_gro_network_header(skb);
-+ net = dev_net(skb->dev);
-+ sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
-+ &hdr->saddr, th->source,
-+ &hdr->daddr, ntohs(th->dest),
-+ iif, sdif);
-+ NAPI_GRO_CB(skb)->is_flist = !sk;
-+ if (sk)
-+ sock_put(sk);
-+#endif /* IS_ENABLED(CONFIG_IPV6) */
-+}
-+
- INDIRECT_CALLABLE_SCOPE
- struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
- {
-+ struct tcphdr *th;
-+
- /* Don't bother verifying checksum if we're going to flush anyway. */
- if (!NAPI_GRO_CB(skb)->flush &&
- skb_gro_checksum_validate(skb, IPPROTO_TCP,
-- ip6_gro_compute_pseudo)) {
-- NAPI_GRO_CB(skb)->flush = 1;
-- return NULL;
-- }
-+ ip6_gro_compute_pseudo))
-+ goto flush;
-
-- return tcp_gro_receive(head, skb);
-+ th = tcp_gro_pull_header(skb);
-+ if (!th)
-+ goto flush;
-+
-+ tcp6_check_fraglist_gro(head, skb, th);
-+
-+ return tcp_gro_receive(head, skb, th);
-+
-+flush:
-+ NAPI_GRO_CB(skb)->flush = 1;
-+ return NULL;
- }
-
- INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
-@@ -32,6 +75,15 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
- const struct ipv6hdr *iph = ipv6_hdr(skb);
- struct tcphdr *th = tcp_hdr(skb);
-
-+ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) {
-+ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV6;
-+ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
-+
-+ __skb_incr_checksum_unnecessary(skb);
-+
-+ return 0;
-+ }
-+
- th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr,
- &iph->daddr, 0);
- skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
-@@ -40,6 +92,61 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
- return 0;
- }
-
-+static void __tcpv6_gso_segment_csum(struct sk_buff *seg,
-+ __be16 *oldport, __be16 newport)
-+{
-+ struct tcphdr *th;
-+
-+ if (*oldport == newport)
-+ return;
-+
-+ th = tcp_hdr(seg);
-+ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
-+ *oldport = newport;
-+}
-+
-+static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs)
-+{
-+ const struct tcphdr *th;
-+ const struct ipv6hdr *iph;
-+ struct sk_buff *seg;
-+ struct tcphdr *th2;
-+ struct ipv6hdr *iph2;
-+
-+ seg = segs;
-+ th = tcp_hdr(seg);
-+ iph = ipv6_hdr(seg);
-+ th2 = tcp_hdr(seg->next);
-+ iph2 = ipv6_hdr(seg->next);
-+
-+ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
-+ ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
-+ ipv6_addr_equal(&iph->daddr, &iph2->daddr))
-+ return segs;
-+
-+ while ((seg = seg->next)) {
-+ th2 = tcp_hdr(seg);
-+ iph2 = ipv6_hdr(seg);
-+
-+ iph2->saddr = iph->saddr;
-+ iph2->daddr = iph->daddr;
-+ __tcpv6_gso_segment_csum(seg, &th2->source, th->source);
-+ __tcpv6_gso_segment_csum(seg, &th2->dest, th->dest);
-+ }
-+
-+ return segs;
-+}
-+
-+static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb,
-+ netdev_features_t features)
-+{
-+ skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
-+ if (IS_ERR(skb))
-+ return skb;
-+
-+ return __tcpv6_gso_segment_list_csum(skb);
-+}
-+
- static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
- netdev_features_t features)
- {
-@@ -51,6 +158,9 @@ static struct sk_buff *tcp6_gso_segment(
- if (!pskb_may_pull(skb, sizeof(*th)))
- return ERR_PTR(-EINVAL);
-
-+ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
-+ return __tcp6_gso_segment_list(skb, features);
-+
- if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
- const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
- struct tcphdr *th = tcp_hdr(skb);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 24 Feb 2025 12:18:23 +0100
-Subject: [PATCH] net: ipv6: fix TCP GSO segmentation with NAT
-
-When updating the source/destination address, the TCP/UDP checksum needs to
-be updated as well.
-
-Fixes: bee88cd5bd83 ("net: add support for segmenting TCP fraglist GSO packets")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/ipv6/tcpv6_offload.c
-+++ b/net/ipv6/tcpv6_offload.c
-@@ -112,24 +112,36 @@ static struct sk_buff *__tcpv6_gso_segme
- struct sk_buff *seg;
- struct tcphdr *th2;
- struct ipv6hdr *iph2;
-+ bool addr_equal;
-
- seg = segs;
- th = tcp_hdr(seg);
- iph = ipv6_hdr(seg);
- th2 = tcp_hdr(seg->next);
- iph2 = ipv6_hdr(seg->next);
-+ addr_equal = ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
-+ ipv6_addr_equal(&iph->daddr, &iph2->daddr);
-
- if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
-- ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
-- ipv6_addr_equal(&iph->daddr, &iph2->daddr))
-+ addr_equal)
- return segs;
-
- while ((seg = seg->next)) {
- th2 = tcp_hdr(seg);
- iph2 = ipv6_hdr(seg);
-
-- iph2->saddr = iph->saddr;
-- iph2->daddr = iph->daddr;
-+ if (!addr_equal) {
-+ inet_proto_csum_replace16(&th2->check, seg,
-+ iph2->saddr.s6_addr32,
-+ iph->saddr.s6_addr32,
-+ true);
-+ inet_proto_csum_replace16(&th2->check, seg,
-+ iph2->daddr.s6_addr32,
-+ iph->daddr.s6_addr32,
-+ true);
-+ iph2->saddr = iph->saddr;
-+ iph2->daddr = iph->daddr;
-+ }
- __tcpv6_gso_segment_csum(seg, &th2->source, th->source);
- __tcpv6_gso_segment_csum(seg, &th2->dest, th->dest);
- }
}
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
-@@ -486,47 +486,14 @@ static struct nft_expr_type nft_flow_off
+@@ -487,47 +487,14 @@ static struct nft_expr_type nft_flow_off
.owner = THIS_MODULE,
};
+++ /dev/null
-From patchwork Sat Oct 26 13:52:25 2024
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 13852245
-X-Patchwork-Delegate: kuba@kernel.org
-Date: Sat, 26 Oct 2024 14:52:25 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-mediatek@lists.infradead.org,
- linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org,
- netdev@vger.kernel.org, Sujuan Chen <sujuan.chen@mediatek.com>,
- AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>,
- Matthias Brugger <matthias.bgg@gmail.com>,
- Paolo Abeni <pabeni@redhat.com>, Jakub Kicinski <kuba@kernel.org>,
- Eric Dumazet <edumazet@google.com>,
- "David S. Miller" <davem@davemloft.net>,
- Andrew Lunn <andrew+netdev@lunn.ch>,
- Lorenzo Bianconi <lorenzo@kernel.org>,
- Mark Lee <Mark-MC.Lee@mediatek.com>,
- Sean Wang <sean.wang@mediatek.com>, Felix Fietkau <nbd@nbd.name>,
- John Crispin <john@phrozen.org>
-Subject: [PATCH net] net: ethernet: mtk_wed: fix path of MT7988 WO firmware
-Message-ID: <Zxz0GWTR5X5LdWPe@pidgin.makrotopia.org>
-Precedence: bulk
-X-Mailing-List: netdev@vger.kernel.org
-List-Id: <netdev.vger.kernel.org>
-List-Subscribe: <mailto:netdev+subscribe@vger.kernel.org>
-List-Unsubscribe: <mailto:netdev+unsubscribe@vger.kernel.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-X-Patchwork-Delegate: kuba@kernel.org
-
-linux-firmware commit 808cba84 ("mtk_wed: add firmware for mt7988
-Wireless Ethernet Dispatcher") added mt7988_wo_{0,1}.bin in the
-'mediatek/mt7988' directory while driver current expects the files in
-the 'mediatek' directory.
-
-Change path in the driver header now that the firmware has been added.
-
-Fixes: e2f64db13aa1 ("net: ethernet: mtk_wed: introduce WED support for MT7988")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
----
- drivers/net/ethernet/mediatek/mtk_wed_wo.h | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-@@ -91,8 +91,8 @@ enum mtk_wed_dummy_cr_idx {
- #define MT7981_FIRMWARE_WO "mediatek/mt7981_wo.bin"
- #define MT7986_FIRMWARE_WO0 "mediatek/mt7986_wo_0.bin"
- #define MT7986_FIRMWARE_WO1 "mediatek/mt7986_wo_1.bin"
--#define MT7988_FIRMWARE_WO0 "mediatek/mt7988_wo_0.bin"
--#define MT7988_FIRMWARE_WO1 "mediatek/mt7988_wo_1.bin"
-+#define MT7988_FIRMWARE_WO0 "mediatek/mt7988/mt7988_wo_0.bin"
-+#define MT7988_FIRMWARE_WO1 "mediatek/mt7988/mt7988_wo_1.bin"
-
- #define MTK_WO_MCU_CFG_LS_BASE 0
- #define MTK_WO_MCU_CFG_LS_HW_VER_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x000)
+++ /dev/null
-From patchwork Thu Feb 1 21:53:06 2024
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 13541843
-Date: Thu, 1 Feb 2024 21:53:06 +0000
-From: Daniel Golle <daniel@makrotopia.org>
-To: Bc-bocun Chen <bc-bocun.chen@mediatek.com>,
- Chunfeng Yun <chunfeng.yun@mediatek.com>,
- Vinod Koul <vkoul@kernel.org>,
- Kishon Vijay Abraham I <kishon@kernel.org>,
- Rob Herring <robh@kernel.org>,
- Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
- Conor Dooley <conor+dt@kernel.org>,
- Daniel Golle <daniel@makrotopia.org>,
- Qingfang Deng <dqfext@gmail.com>,
- SkyLake Huang <SkyLake.Huang@mediatek.com>,
- Matthias Brugger <matthias.bgg@gmail.com>,
- AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>,
- Philipp Zabel <p.zabel@pengutronix.de>,
- linux-arm-kernel@lists.infradead.org,
- linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org,
- devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
- netdev@vger.kernel.org
-Subject: [PATCH 2/2] phy: add driver for MediaTek XFI T-PHY
-Message-ID:
- <dd6b40ea1f7f8459a9a2cfe7fa60c1108332ade6.1706823233.git.daniel@makrotopia.org>
-References:
- <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To:
- <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org>
-List-Id: Linux Phy Mailing list <linux-phy.lists.infradead.org>
-
-Add driver for MediaTek's XFI T-PHY, 10 Gigabit/s Ethernet SerDes PHY
-which can be found in the MT7988 SoC.
-
-The PHY can operates only in PHY_MODE_ETHERNET, the submode is one of
-PHY_INTERFACE_MODE_* corresponding to the supported modes:
-
- * USXGMII \
- * 10GBase-R }- USXGMII PCS - XGDM \
- * 5GBase-R / \
- }- Ethernet MAC
- * 2500Base-X \ /
- * 1000Base-X }- LynxI PCS - GDM /
- * Cisco SGMII (MAC side) /
-
-In order to work-around a performance issue present on the first of
-two XFI T-PHYs present in MT7988, special tuning is applied which can be
-selected by adding the 'mediatek,usxgmii-performance-errata' property to
-the device tree node.
-
-There is no documentation for most registers used for the
-analog/tuning part, however, most of the registers have been partially
-reverse-engineered from MediaTek's SDK implementation (an opaque
-sequence of 32-bit register writes) and descriptions for all relevant
-digital registers and bits such as resets and muxes have been supplied
-by MediaTek.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
----
- MAINTAINERS | 1 +
- drivers/phy/mediatek/Kconfig | 12 +
- drivers/phy/mediatek/Makefile | 1 +
- drivers/phy/mediatek/phy-mtk-xfi-tphy.c | 392 ++++++++++++++++++++++++
- 4 files changed, 406 insertions(+)
- create mode 100644 drivers/phy/mediatek/phy-mtk-xfi-tphy.c
-
---- a/drivers/phy/mediatek/Kconfig
-+++ b/drivers/phy/mediatek/Kconfig
-@@ -13,6 +13,18 @@ config PHY_MTK_PCIE
- callback for PCIe GEN3 port, it supports software efuse
- initialization.
-
-+config PHY_MTK_XFI_TPHY
-+ tristate "MediaTek XFI T-PHY Driver"
-+ depends on ARCH_MEDIATEK || COMPILE_TEST
-+ depends on OF && OF_ADDRESS
-+ depends on HAS_IOMEM
-+ select GENERIC_PHY
-+ help
-+ Say 'Y' here to add support for MediaTek XFI T-PHY driver.
-+ The driver provides access to the Ethernet SerDes T-PHY supporting
-+ 1GE and 2.5GE modes via the LynxI PCS, and 5GE and 10GE modes
-+ via the USXGMII PCS found in MediaTek SoCs with 10G Ethernet.
-+
- config PHY_MTK_TPHY
- tristate "MediaTek T-PHY Driver"
- depends on ARCH_MEDIATEK || COMPILE_TEST
---- a/drivers/phy/mediatek/Makefile
-+++ b/drivers/phy/mediatek/Makefile
-@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-p
- obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
- obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o
- obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o
-+obj-$(CONFIG_PHY_MTK_XFI_TPHY) += phy-mtk-xfi-tphy.o
-
- phy-mtk-hdmi-drv-y := phy-mtk-hdmi.o
- phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt2701.o
---- /dev/null
-+++ b/drivers/phy/mediatek/phy-mtk-xfi-tphy.c
-@@ -0,0 +1,393 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/* MediaTek 10GE SerDes PHY driver
-+ *
-+ * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
-+ * Bc-bocun Chen <bc-bocun.chen@mediatek.com>
-+ * based on mtk_usxgmii.c found in MediaTek's SDK released under GPL-2.0
-+ * Copyright (c) 2022 MediaTek Inc.
-+ * Author: Henry Yen <henry.yen@mediatek.com>
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/device.h>
-+#include <linux/platform_device.h>
-+#include <linux/of.h>
-+#include <linux/io.h>
-+#include <linux/clk.h>
-+#include <linux/reset.h>
-+#include <linux/phy.h>
-+#include <linux/phy/phy.h>
-+
-+#define MTK_XFI_TPHY_NUM_CLOCKS 2
-+
-+#define REG_DIG_GLB_70 0x0070
-+#define XTP_PCS_RX_EQ_IN_PROGRESS(x) FIELD_PREP(GENMASK(25, 24), (x))
-+#define XTP_PCS_MODE_MASK GENMASK(17, 16)
-+#define XTP_PCS_MODE(x) FIELD_PREP(GENMASK(17, 16), (x))
-+#define XTP_PCS_RST_B BIT(15)
-+#define XTP_FRC_PCS_RST_B BIT(14)
-+#define XTP_PCS_PWD_SYNC_MASK GENMASK(13, 12)
-+#define XTP_PCS_PWD_SYNC(x) FIELD_PREP(XTP_PCS_PWD_SYNC_MASK, (x))
-+#define XTP_PCS_PWD_ASYNC_MASK GENMASK(11, 10)
-+#define XTP_PCS_PWD_ASYNC(x) FIELD_PREP(XTP_PCS_PWD_ASYNC_MASK, (x))
-+#define XTP_FRC_PCS_PWD_ASYNC BIT(8)
-+#define XTP_PCS_UPDT BIT(4)
-+#define XTP_PCS_IN_FR_RG BIT(0)
-+
-+#define REG_DIG_GLB_F4 0x00f4
-+#define XFI_DPHY_PCS_SEL BIT(0)
-+#define XFI_DPHY_PCS_SEL_SGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 1)
-+#define XFI_DPHY_PCS_SEL_USXGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 0)
-+#define XFI_DPHY_AD_SGDT_FRC_EN BIT(5)
-+
-+#define REG_DIG_LN_TRX_40 0x3040
-+#define XTP_LN_FRC_TX_DATA_EN BIT(29)
-+#define XTP_LN_TX_DATA_EN BIT(28)
-+
-+#define REG_DIG_LN_TRX_B0 0x30b0
-+#define XTP_LN_FRC_TX_MACCK_EN BIT(5)
-+#define XTP_LN_TX_MACCK_EN BIT(4)
-+
-+#define REG_ANA_GLB_D0 0x90d0
-+#define XTP_GLB_USXGMII_SEL_MASK GENMASK(3, 1)
-+#define XTP_GLB_USXGMII_SEL(x) FIELD_PREP(GENMASK(3, 1), (x))
-+#define XTP_GLB_USXGMII_EN BIT(0)
-+
-+struct mtk_xfi_tphy {
-+ void __iomem *base;
-+ struct device *dev;
-+ struct reset_control *reset;
-+ struct clk_bulk_data clocks[MTK_XFI_TPHY_NUM_CLOCKS];
-+ bool da_war;
-+};
-+
-+static void mtk_xfi_tphy_write(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
-+ u32 value)
-+{
-+ iowrite32(value, xfi_tphy->base + reg);
-+}
-+
-+static void mtk_xfi_tphy_rmw(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
-+ u32 clr, u32 set)
-+{
-+ u32 val;
-+
-+ val = ioread32(xfi_tphy->base + reg);
-+ val &= ~clr;
-+ val |= set;
-+ iowrite32(val, xfi_tphy->base + reg);
-+}
-+
-+static void mtk_xfi_tphy_set(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
-+ u32 set)
-+{
-+ mtk_xfi_tphy_rmw(xfi_tphy, reg, 0, set);
-+}
-+
-+static void mtk_xfi_tphy_clear(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
-+ u32 clr)
-+{
-+ mtk_xfi_tphy_rmw(xfi_tphy, reg, clr, 0);
-+}
-+
-+static void mtk_xfi_tphy_setup(struct mtk_xfi_tphy *xfi_tphy,
-+ phy_interface_t interface)
-+{
-+ bool is_2p5g = (interface == PHY_INTERFACE_MODE_2500BASEX);
-+ bool is_1g = (interface == PHY_INTERFACE_MODE_1000BASEX ||
-+ interface == PHY_INTERFACE_MODE_SGMII);
-+ bool is_10g = (interface == PHY_INTERFACE_MODE_10GBASER ||
-+ interface == PHY_INTERFACE_MODE_USXGMII);
-+ bool is_5g = (interface == PHY_INTERFACE_MODE_5GBASER);
-+ bool is_xgmii = (is_10g || is_5g);
-+
-+ dev_dbg(xfi_tphy->dev, "setting up for mode %s\n", phy_modes(interface));
-+
-+ /* Setup PLL setting */
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x9024, 0x100000, is_10g ? 0x0 : 0x100000);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x2020, 0x202000, is_5g ? 0x202000 : 0x0);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x2030, 0x500, is_1g ? 0x0 : 0x500);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x2034, 0xa00, is_1g ? 0x0 : 0xa00);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x2040, 0x340000, is_1g ? 0x200000 :
-+ 0x140000);
-+
-+ /* Setup RXFE BW setting */
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50f0, 0xc10, is_1g ? 0x410 :
-+ is_5g ? 0x800 : 0x400);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e0, 0x4000, is_5g ? 0x0 : 0x4000);
-+
-+ /* Setup RX CDR setting */
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x506c, 0x30000, is_5g ? 0x0 : 0x30000);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5070, 0x670000, is_5g ? 0x620000 : 0x50000);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5074, 0x180000, is_5g ? 0x180000 : 0x0);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5078, 0xf000400, is_5g ? 0x8000000 :
-+ 0x7000400);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x507c, 0x5000500, is_5g ? 0x4000400 :
-+ 0x1000100);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5080, 0x1410, is_1g ? 0x400 :
-+ is_5g ? 0x1010 : 0x0);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5084, 0x30300, is_1g ? 0x30300 :
-+ is_5g ? 0x30100 :
-+ 0x100);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5088, 0x60200, is_1g ? 0x20200 :
-+ is_5g ? 0x40000 :
-+ 0x20000);
-+
-+ /* Setting RXFE adaptation range setting */
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e4, 0xc0000, is_5g ? 0x0 : 0xc0000);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e8, 0x40000, is_5g ? 0x0 : 0x40000);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50ec, 0xa00, is_1g ? 0x200 : 0x800);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50a8, 0xee0000, is_5g ? 0x800000 :
-+ 0x6e0000);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x6004, 0x190000, is_5g ? 0x0 : 0x190000);
-+ if (is_10g)
-+ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x01423342);
-+ else if (is_5g)
-+ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x00a132a1);
-+ else if (is_2p5g)
-+ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x009c329c);
-+ else
-+ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x00fa32fa);
-+
-+ /* Force SGDT_OUT off and select PCS */
-+ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_F4,
-+ XFI_DPHY_AD_SGDT_FRC_EN | XFI_DPHY_PCS_SEL,
-+ XFI_DPHY_AD_SGDT_FRC_EN |
-+ (is_xgmii ? XFI_DPHY_PCS_SEL_USXGMII :
-+ XFI_DPHY_PCS_SEL_SGMII));
-+
-+
-+ /* Force GLB_CKDET_OUT */
-+ mtk_xfi_tphy_set(xfi_tphy, 0x0030, 0xc00);
-+
-+ /* Force AEQ on */
-+ mtk_xfi_tphy_write(xfi_tphy, REG_DIG_GLB_70,
-+ XTP_PCS_RX_EQ_IN_PROGRESS(2) |
-+ XTP_PCS_PWD_SYNC(2) |
-+ XTP_PCS_PWD_ASYNC(2));
-+
-+ usleep_range(1, 5);
-+ writel(XTP_LN_FRC_TX_DATA_EN, xfi_tphy->base + REG_DIG_LN_TRX_40);
-+
-+ /* Setup TX DA default value */
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x30b0, 0x30, 0x20);
-+ mtk_xfi_tphy_write(xfi_tphy, 0x3028, 0x00008a01);
-+ mtk_xfi_tphy_write(xfi_tphy, 0x302c, 0x0000a884);
-+ mtk_xfi_tphy_write(xfi_tphy, 0x3024, 0x00083002);
-+
-+ /* Setup RG default value */
-+ if (is_xgmii) {
-+ mtk_xfi_tphy_write(xfi_tphy, 0x3010, 0x00022220);
-+ mtk_xfi_tphy_write(xfi_tphy, 0x5064, 0x0f020a01);
-+ mtk_xfi_tphy_write(xfi_tphy, 0x50b4, 0x06100600);
-+ if (interface == PHY_INTERFACE_MODE_USXGMII)
-+ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x40704000);
-+ else
-+ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x47684100);
-+ } else {
-+ mtk_xfi_tphy_write(xfi_tphy, 0x3010, 0x00011110);
-+ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x40704000);
-+ }
-+
-+ if (is_1g)
-+ mtk_xfi_tphy_write(xfi_tphy, 0x3064, 0x0000c000);
-+
-+ /* Setup RX EQ initial value */
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x3050, 0xa8000000,
-+ (interface != PHY_INTERFACE_MODE_10GBASER) ?
-+ 0xa8000000 : 0x0);
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0x3054, 0xaa,
-+ (interface != PHY_INTERFACE_MODE_10GBASER) ?
-+ 0xaa : 0x0);
-+
-+ if (is_xgmii)
-+ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x00000f00);
-+ else if (is_2p5g)
-+ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x22000f00);
-+ else
-+ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x20200f00);
-+
-+ if (interface == PHY_INTERFACE_MODE_10GBASER && xfi_tphy->da_war)
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0xa008, 0x10000, 0x10000);
-+
-+ mtk_xfi_tphy_rmw(xfi_tphy, 0xa060, 0x50000, is_xgmii ? 0x40000 :
-+ 0x50000);
-+
-+ /* Setup PHYA speed */
-+ mtk_xfi_tphy_rmw(xfi_tphy, REG_ANA_GLB_D0,
-+ XTP_GLB_USXGMII_SEL_MASK | XTP_GLB_USXGMII_EN,
-+ is_10g ? XTP_GLB_USXGMII_SEL(0) :
-+ is_5g ? XTP_GLB_USXGMII_SEL(1) :
-+ is_2p5g ? XTP_GLB_USXGMII_SEL(2) :
-+ XTP_GLB_USXGMII_SEL(3));
-+ mtk_xfi_tphy_set(xfi_tphy, REG_ANA_GLB_D0, XTP_GLB_USXGMII_EN);
-+
-+ /* Release reset */
-+ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_GLB_70,
-+ XTP_PCS_RST_B | XTP_FRC_PCS_RST_B);
-+ usleep_range(150, 500);
-+
-+ /* Switch to P0 */
-+ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70,
-+ XTP_PCS_PWD_SYNC_MASK |
-+ XTP_PCS_PWD_ASYNC_MASK,
-+ XTP_FRC_PCS_PWD_ASYNC |
-+ XTP_PCS_UPDT | XTP_PCS_IN_FR_RG);
-+ usleep_range(1, 5);
-+
-+ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_70, XTP_PCS_UPDT);
-+ usleep_range(15, 50);
-+
-+ if (is_xgmii) {
-+ /* Switch to Gen3 */
-+ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70,
-+ XTP_PCS_MODE_MASK | XTP_PCS_UPDT,
-+ XTP_PCS_MODE(2) | XTP_PCS_UPDT);
-+ } else {
-+ /* Switch to Gen2 */
-+ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70,
-+ XTP_PCS_MODE_MASK | XTP_PCS_UPDT,
-+ XTP_PCS_MODE(1) | XTP_PCS_UPDT);
-+ }
-+ usleep_range(1, 5);
-+
-+ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_70, XTP_PCS_UPDT);
-+
-+ usleep_range(100, 500);
-+
-+ /* Enable MAC CK */
-+ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_LN_TRX_B0, XTP_LN_TX_MACCK_EN);
-+ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_F4, XFI_DPHY_AD_SGDT_FRC_EN);
-+
-+ /* Enable TX data */
-+ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_LN_TRX_40,
-+ XTP_LN_FRC_TX_DATA_EN | XTP_LN_TX_DATA_EN);
-+ usleep_range(400, 1000);
-+}
-+
-+static int mtk_xfi_tphy_set_mode(struct phy *phy, enum phy_mode mode, int
-+ submode)
-+{
-+ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
-+
-+ if (mode != PHY_MODE_ETHERNET)
-+ return -EINVAL;
-+
-+ switch (submode) {
-+ case PHY_INTERFACE_MODE_1000BASEX:
-+ case PHY_INTERFACE_MODE_2500BASEX:
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_5GBASER:
-+ case PHY_INTERFACE_MODE_10GBASER:
-+ case PHY_INTERFACE_MODE_USXGMII:
-+ mtk_xfi_tphy_setup(xfi_tphy, submode);
-+ return 0;
-+ default:
-+ return -EINVAL;
-+ }
-+}
-+
-+static int mtk_xfi_tphy_reset(struct phy *phy)
-+{
-+ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
-+
-+ reset_control_assert(xfi_tphy->reset);
-+ usleep_range(100, 500);
-+ reset_control_deassert(xfi_tphy->reset);
-+ usleep_range(1, 10);
-+
-+ return 0;
-+}
-+
-+static int mtk_xfi_tphy_power_on(struct phy *phy)
-+{
-+ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
-+
-+ return clk_bulk_prepare_enable(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
-+}
-+
-+static int mtk_xfi_tphy_power_off(struct phy *phy)
-+{
-+ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
-+
-+ clk_bulk_disable_unprepare(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
-+
-+ return 0;
-+}
-+
-+static const struct phy_ops mtk_xfi_tphy_ops = {
-+ .power_on = mtk_xfi_tphy_power_on,
-+ .power_off = mtk_xfi_tphy_power_off,
-+ .set_mode = mtk_xfi_tphy_set_mode,
-+ .reset = mtk_xfi_tphy_reset,
-+ .owner = THIS_MODULE,
-+};
-+
-+static int mtk_xfi_tphy_probe(struct platform_device *pdev)
-+{
-+ struct device_node *np = pdev->dev.of_node;
-+ struct phy_provider *phy_provider;
-+ struct mtk_xfi_tphy *xfi_tphy;
-+ struct phy *phy;
-+
-+ if (!np)
-+ return -ENODEV;
-+
-+ xfi_tphy = devm_kzalloc(&pdev->dev, sizeof(*xfi_tphy), GFP_KERNEL);
-+ if (!xfi_tphy)
-+ return -ENOMEM;
-+
-+ xfi_tphy->base = devm_of_iomap(&pdev->dev, np, 0, NULL);
-+ if (!xfi_tphy->base)
-+ return -EIO;
-+
-+ xfi_tphy->dev = &pdev->dev;
-+
-+ xfi_tphy->clocks[0].id = "topxtal";
-+ xfi_tphy->clocks[0].clk = devm_clk_get(&pdev->dev, xfi_tphy->clocks[0].id);
-+ if (IS_ERR(xfi_tphy->clocks[0].clk))
-+ return PTR_ERR(xfi_tphy->clocks[0].clk);
-+
-+ xfi_tphy->clocks[1].id = "xfipll";
-+ xfi_tphy->clocks[1].clk = devm_clk_get(&pdev->dev, xfi_tphy->clocks[1].id);
-+ if (IS_ERR(xfi_tphy->clocks[1].clk))
-+ return PTR_ERR(xfi_tphy->clocks[1].clk);
-+
-+ xfi_tphy->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
-+ if (IS_ERR(xfi_tphy->reset))
-+ return PTR_ERR(xfi_tphy->reset);
-+
-+ xfi_tphy->da_war = of_property_read_bool(np,
-+ "mediatek,usxgmii-performance-errata");
-+
-+ phy = devm_phy_create(&pdev->dev, NULL, &mtk_xfi_tphy_ops);
-+ if (IS_ERR(phy))
-+ return PTR_ERR(phy);
-+
-+ phy_set_drvdata(phy, xfi_tphy);
-+
-+ phy_provider = devm_of_phy_provider_register(&pdev->dev,
-+ of_phy_simple_xlate);
-+
-+ return PTR_ERR_OR_ZERO(phy_provider);
-+}
-+
-+static const struct of_device_id mtk_xfi_tphy_match[] = {
-+ { .compatible = "mediatek,mt7988-xfi-tphy", },
-+ { }
-+};
-+MODULE_DEVICE_TABLE(of, mtk_xfi_tphy_match);
-+
-+static struct platform_driver mtk_xfi_tphy_driver = {
-+ .probe = mtk_xfi_tphy_probe,
-+ .driver = {
-+ .name = "mtk-xfi-tphy",
-+ .of_match_table = mtk_xfi_tphy_match,
-+ },
-+};
-+module_platform_driver(mtk_xfi_tphy_driver);
-+
-+MODULE_DESCRIPTION("MediaTek XFI T-PHY driver");
-+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
-+MODULE_AUTHOR("Bc-bocun Chen <bc-bocun.chen@mediatek.com>");
-+MODULE_LICENSE("GPL");
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -17716,6 +17716,13 @@ L: netdev@vger.kernel.org
+@@ -17717,6 +17717,13 @@ L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/ethernet/qualcomm/emac/
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -17722,6 +17722,7 @@ L: netdev@vger.kernel.org
+@@ -17723,6 +17723,7 @@ L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/qcom,ipq4019-ess-edma.yaml
F: drivers/net/ethernet/qualcomm/ipqess/
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
-@@ -1570,6 +1570,10 @@ static void sfp_hwmon_probe(struct work_
+@@ -1571,6 +1571,10 @@ static void sfp_hwmon_probe(struct work_
struct sfp *sfp = container_of(work, struct sfp, hwmon_probe.work);
int err;
/* hwmon interface needs to access 16bit registers in atomic way to
* guarantee coherency of the diagnostic monitoring data. If it is not
* possible to guarantee coherency because EEPROM is broken in such way
-@@ -2430,6 +2434,13 @@ static void sfp_sm_module(struct sfp *sf
+@@ -2433,6 +2437,13 @@ static void sfp_sm_module(struct sfp *sf
return;
}
static void sfp_i2c_mdiobus_destroy(struct sfp *sfp)
{
mdiobus_unregister(sfp->i2c_mii);
-@@ -1888,9 +1965,15 @@ static void sfp_sm_fault(struct sfp *sfp
+@@ -1889,9 +1966,15 @@ static void sfp_sm_fault(struct sfp *sfp
static int sfp_sm_add_mdio_bus(struct sfp *sfp)
{
DRM DRIVERS FOR STI
M: Alain Volmat <alain.volmat@foss.st.com>
L: dri-devel@lists.freedesktop.org
-@@ -16016,6 +16024,13 @@ F: Documentation/i2c/busses/i2c-ocores.r
+@@ -16017,6 +16025,13 @@ F: Documentation/i2c/busses/i2c-ocores.r
F: drivers/i2c/busses/i2c-ocores.c
F: include/linux/platform_data/i2c-ocores.h
OPENRISC ARCHITECTURE
M: Jonas Bonn <jonas@southpole.se>
M: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
-@@ -16427,6 +16442,14 @@ S: Maintained
+@@ -16428,6 +16443,14 @@ S: Maintained
F: Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt
F: drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c
PCI DRIVER FOR RENESAS R-CAR
M: Marek Vasut <marek.vasut+renesas@gmail.com>
M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
-@@ -16658,7 +16681,7 @@ M: Daire McNamara <daire.mcnamara@microc
+@@ -16659,7 +16682,7 @@ M: Daire McNamara <daire.mcnamara@microc
L: linux-pci@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/pci/microchip*
PCIE DRIVER FOR QUALCOMM MSM
M: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-@@ -16682,6 +16705,13 @@ S: Maintained
+@@ -16683,6 +16706,13 @@ S: Maintained
F: Documentation/devicetree/bindings/pci/socionext,uniphier-pcie*
F: drivers/pci/controller/dwc/pcie-uniphier*
PCIE DRIVER FOR ST SPEAR13XX
M: Pratyush Anand <pratyush.anand@gmail.com>
L: linux-pci@vger.kernel.org
-@@ -18454,7 +18484,7 @@ F: drivers/char/hw_random/mpfs-rng.c
+@@ -18455,7 +18485,7 @@ F: drivers/char/hw_random/mpfs-rng.c
F: drivers/clk/microchip/clk-mpfs*.c
F: drivers/i2c/busses/i2c-microchip-corei2c.c
F: drivers/mailbox/mailbox-mpfs.c
F: drivers/pwm/pwm-microchip-core.c
F: drivers/reset/reset-mpfs.c
F: drivers/rtc/rtc-mpfs.c
-@@ -20435,6 +20465,15 @@ M: Ion Badulescu <ionut@badula.org>
+@@ -20436,6 +20466,15 @@ M: Ion Badulescu <ionut@badula.org>
S: Odd Fixes
F: drivers/net/ethernet/adaptec/starfire*
STARFIVE CRYPTO DRIVER
M: Jia Jie Ho <jiajie.ho@starfivetech.com>
M: William Qiu <william.qiu@starfivetech.com>
-@@ -20473,6 +20512,13 @@ S: Supported
+@@ -20474,6 +20513,13 @@ S: Supported
F: Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml
F: drivers/clk/starfive/clk-starfive-jh7110-pll.c
STARFIVE JH7110 SYSCON
M: William Qiu <william.qiu@starfivetech.com>
M: Xingyu Wu <xingyu.wu@starfivetech.com>
-@@ -20520,9 +20566,10 @@ F: drivers/usb/cdns3/cdns3-starfive.c
+@@ -20521,9 +20567,10 @@ F: drivers/usb/cdns3/cdns3-starfive.c
STARFIVE JH71XX PMU CONTROLLER DRIVER
M: Walker Chen <walker.chen@starfivetech.com>
F: include/dt-bindings/power/starfive,jh7110-pmu.h
STARFIVE SOC DRIVERS
-@@ -20530,7 +20577,13 @@ M: Conor Dooley <conor@kernel.org>
+@@ -20531,7 +20578,13 @@ M: Conor Dooley <conor@kernel.org>
S: Maintained
T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/
F: Documentation/devicetree/bindings/soc/starfive/
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -20554,7 +20554,7 @@ STARFIVE JH71X0 RESET CONTROLLER DRIVERS
+@@ -20555,7 +20555,7 @@ STARFIVE JH71X0 RESET CONTROLLER DRIVERS
M: Emil Renner Berthing <kernel@esmil.dk>
M: Hal Feng <hal.feng@starfivetech.com>
S: Maintained