+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Subject: mips: replace -mlong-calls with -mno-long-calls to make function calls faster in kernel modules to achieve this, try to
-
-lede-commit: 3b3d64743ba2a874df9d70cd19e242205b0a788c
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
- arch/mips/Makefile | 5 +
- arch/mips/include/asm/module.h | 5 +
- arch/mips/kernel/module.c | 279 ++++++++++++++++++++++++++++++++++++++++-
- 3 files changed, 284 insertions(+), 5 deletions(-)
-
---- a/arch/mips/Makefile
-+++ b/arch/mips/Makefile
-@@ -97,8 +97,18 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin
- cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely
- cflags-y += -msoft-float -Wa,-msoft-float
- LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
-+ifdef CONFIG_64BIT
- KBUILD_AFLAGS_MODULE += -mlong-calls
- KBUILD_CFLAGS_MODULE += -mlong-calls
-+else
-+ ifdef CONFIG_DYNAMIC_FTRACE
-+ KBUILD_AFLAGS_MODULE += -mlong-calls
-+ KBUILD_CFLAGS_MODULE += -mlong-calls
-+ else
-+ KBUILD_AFLAGS_MODULE += -mno-long-calls
-+ KBUILD_CFLAGS_MODULE += -mno-long-calls
-+ endif
-+endif
-
- ifeq ($(CONFIG_RELOCATABLE),y)
- LDFLAGS_vmlinux += --emit-relocs
---- a/arch/mips/include/asm/module.h
-+++ b/arch/mips/include/asm/module.h
-@@ -12,6 +12,11 @@ struct mod_arch_specific {
- const struct exception_table_entry *dbe_start;
- const struct exception_table_entry *dbe_end;
- struct mips_hi16 *r_mips_hi16_list;
-+
-+ void *phys_plt_tbl;
-+ void *virt_plt_tbl;
-+ unsigned int phys_plt_offset;
-+ unsigned int virt_plt_offset;
- };
-
- typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */
---- a/arch/mips/kernel/module.c
-+++ b/arch/mips/kernel/module.c
-@@ -32,23 +32,261 @@ struct mips_hi16 {
- static LIST_HEAD(dbe_list);
- static DEFINE_SPINLOCK(dbe_lock);
-
--#ifdef MODULE_START
-+/*
-+ * Get the potential max trampolines size required of the init and
-+ * non-init sections. Only used if we cannot find enough contiguous
-+ * physically mapped memory to put the module into.
-+ */
-+static unsigned int
-+get_plt_size(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
-+ const char *secstrings, unsigned int symindex, bool is_init)
-+{
-+ unsigned long ret = 0;
-+ unsigned int i, j;
-+ Elf_Sym *syms;
-+
-+ /* Everything marked ALLOC (this includes the exported symbols) */
-+ for (i = 1; i < hdr->e_shnum; ++i) {
-+ unsigned int info = sechdrs[i].sh_info;
-+
-+ if (sechdrs[i].sh_type != SHT_REL
-+ && sechdrs[i].sh_type != SHT_RELA)
-+ continue;
-+
-+ /* Not a valid relocation section? */
-+ if (info >= hdr->e_shnum)
-+ continue;
-+
-+ /* Don't bother with non-allocated sections */
-+ if (!(sechdrs[info].sh_flags & SHF_ALLOC))
-+ continue;
-+
-+ /* If it's called *.init*, and we're not init, we're
-+ not interested */
-+ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0)
-+ != is_init)
-+ continue;
-+
-+ syms = (Elf_Sym *) sechdrs[symindex].sh_addr;
-+ if (sechdrs[i].sh_type == SHT_REL) {
-+ Elf_Mips_Rel *rel = (void *) sechdrs[i].sh_addr;
-+ unsigned int size = sechdrs[i].sh_size / sizeof(*rel);
-+
-+ for (j = 0; j < size; ++j) {
-+ Elf_Sym *sym;
-+
-+ if (ELF_MIPS_R_TYPE(rel[j]) != R_MIPS_26)
-+ continue;
-+
-+ sym = syms + ELF_MIPS_R_SYM(rel[j]);
-+ if (!is_init && sym->st_shndx != SHN_UNDEF)
-+ continue;
-+
-+ ret += 4 * sizeof(int);
-+ }
-+ } else {
-+ Elf_Mips_Rela *rela = (void *) sechdrs[i].sh_addr;
-+ unsigned int size = sechdrs[i].sh_size / sizeof(*rela);
-+
-+ for (j = 0; j < size; ++j) {
-+ Elf_Sym *sym;
-+
-+ if (ELF_MIPS_R_TYPE(rela[j]) != R_MIPS_26)
-+ continue;
-+
-+ sym = syms + ELF_MIPS_R_SYM(rela[j]);
-+ if (!is_init && sym->st_shndx != SHN_UNDEF)
-+ continue;
-+
-+ ret += 4 * sizeof(int);
-+ }
-+ }
-+ }
-+
-+ return ret;
-+}
-+
-+#ifndef MODULE_START
-+static void *alloc_phys(unsigned long size)
-+{
-+ unsigned order;
-+ struct page *page;
-+ struct page *p;
-+
-+ size = PAGE_ALIGN(size);
-+ order = get_order(size);
-+
-+ page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN |
-+ __GFP_THISNODE, order);
-+ if (!page)
-+ return NULL;
-+
-+ split_page(page, order);
-+
-+ /* mark all pages except for the last one */
-+ for (p = page; p + 1 < page + (size >> PAGE_SHIFT); ++p)
-+ set_bit(PG_owner_priv_1, &p->flags);
-+
-+ for (p = page + (size >> PAGE_SHIFT); p < page + (1 << order); ++p)
-+ __free_page(p);
-+
-+ return page_address(page);
-+}
-+#endif
-+
-+static void free_phys(void *ptr)
-+{
-+ struct page *page;
-+ bool free;
-+
-+ page = virt_to_page(ptr);
-+ do {
-+ free = test_and_clear_bit(PG_owner_priv_1, &page->flags);
-+ __free_page(page);
-+ page++;
-+ } while (free);
-+}
-+
-+
- void *module_alloc(unsigned long size)
- {
-+#ifdef MODULE_START
- return __vmalloc_node_range(size, 1, MODULE_START, MODULE_END,
- GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE,
- __builtin_return_address(0));
-+#else
-+ void *ptr;
-+
-+ if (size == 0)
-+ return NULL;
-+
-+ ptr = alloc_phys(size);
-+
-+ /* If we failed to allocate physically contiguous memory,
-+ * fall back to regular vmalloc. The module loader code will
-+ * create jump tables to handle long jumps */
-+ if (!ptr)
-+ return vmalloc(size);
-+
-+ return ptr;
-+#endif
- }
-+
-+static inline bool is_phys_addr(void *ptr)
-+{
-+#ifdef CONFIG_64BIT
-+ return (KSEGX((unsigned long)ptr) == CKSEG0);
-+#else
-+ return (KSEGX(ptr) == KSEG0);
- #endif
-+}
-+
-+/* Free memory returned from module_alloc */
-+void module_memfree(void *module_region)
-+{
-+ if (is_phys_addr(module_region))
-+ free_phys(module_region);
-+ else
-+ vfree(module_region);
-+}
-+
-+static void *__module_alloc(int size, bool phys)
-+{
-+ void *ptr;
-+
-+ if (phys)
-+ ptr = kmalloc(size, GFP_KERNEL);
-+ else
-+ ptr = vmalloc(size);
-+ return ptr;
-+}
-+
-+static void __module_free(void *ptr)
-+{
-+ if (is_phys_addr(ptr))
-+ kfree(ptr);
-+ else
-+ vfree(ptr);
-+}
-+
-+int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
-+ char *secstrings, struct module *mod)
-+{
-+ unsigned int symindex = 0;
-+ unsigned int core_size, init_size;
-+ int i;
-+
-+ mod->arch.phys_plt_offset = 0;
-+ mod->arch.virt_plt_offset = 0;
-+ mod->arch.phys_plt_tbl = NULL;
-+ mod->arch.virt_plt_tbl = NULL;
-+
-+ if (IS_ENABLED(CONFIG_64BIT))
-+ return 0;
-+
-+ for (i = 1; i < hdr->e_shnum; i++)
-+ if (sechdrs[i].sh_type == SHT_SYMTAB)
-+ symindex = i;
-+
-+ core_size = get_plt_size(hdr, sechdrs, secstrings, symindex, false);
-+ init_size = get_plt_size(hdr, sechdrs, secstrings, symindex, true);
-+
-+ if ((core_size + init_size) == 0)
-+ return 0;
-+
-+ mod->arch.phys_plt_tbl = __module_alloc(core_size + init_size, 1);
-+ if (!mod->arch.phys_plt_tbl)
-+ return -ENOMEM;
-+
-+ mod->arch.virt_plt_tbl = __module_alloc(core_size + init_size, 0);
-+ if (!mod->arch.virt_plt_tbl) {
-+ __module_free(mod->arch.phys_plt_tbl);
-+ mod->arch.phys_plt_tbl = NULL;
-+ return -ENOMEM;
-+ }
-+
-+ return 0;
-+}
-
- static void apply_r_mips_32(u32 *location, u32 base, Elf_Addr v)
- {
- *location = base + v;
- }
-
-+static Elf_Addr add_plt_entry_to(unsigned *plt_offset,
-+ void *start, Elf_Addr v)
-+{
-+ unsigned *tramp = start + *plt_offset;
-+ *plt_offset += 4 * sizeof(int);
-+
-+ /* adjust carry for addiu */
-+ if (v & 0x00008000)
-+ v += 0x10000;
-+
-+ tramp[0] = 0x3c190000 | (v >> 16); /* lui t9, hi16 */
-+ tramp[1] = 0x27390000 | (v & 0xffff); /* addiu t9, t9, lo16 */
-+ tramp[2] = 0x03200008; /* jr t9 */
-+ tramp[3] = 0x00000000; /* nop */
-+
-+ return (Elf_Addr) tramp;
-+}
-+
-+static Elf_Addr add_plt_entry(struct module *me, void *location, Elf_Addr v)
-+{
-+ if (is_phys_addr(location))
-+ return add_plt_entry_to(&me->arch.phys_plt_offset,
-+ me->arch.phys_plt_tbl, v);
-+ else
-+ return add_plt_entry_to(&me->arch.virt_plt_offset,
-+ me->arch.virt_plt_tbl, v);
-+
-+}
-+
- static int apply_r_mips_26(struct module *me, u32 *location, u32 base,
- Elf_Addr v)
- {
-+ u32 ofs = base & 0x03ffffff;
-+
- if (v % 4) {
- pr_err("module %s: dangerous R_MIPS_26 relocation\n",
- me->name);
-@@ -56,13 +294,17 @@ static int apply_r_mips_26(struct module
- }
-
- if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
-- pr_err("module %s: relocation overflow\n",
-- me->name);
-- return -ENOEXEC;
-+ v = add_plt_entry(me, location, v + (ofs << 2));
-+ if (!v) {
-+ pr_err("module %s: relocation overflow\n",
-+ me->name);
-+ return -ENOEXEC;
-+ }
-+ ofs = 0;
- }
-
- *location = (*location & ~0x03ffffff) |
-- ((base + (v >> 2)) & 0x03ffffff);
-+ ((ofs + (v >> 2)) & 0x03ffffff);
-
- return 0;
- }
-@@ -442,9 +684,36 @@ int module_finalize(const Elf_Ehdr *hdr,
- list_add(&me->arch.dbe_list, &dbe_list);
- spin_unlock_irq(&dbe_lock);
- }
-+
-+ /* Get rid of the fixup trampoline if we're running the module
-+ * from physically mapped address space */
-+ if (me->arch.phys_plt_offset == 0) {
-+ __module_free(me->arch.phys_plt_tbl);
-+ me->arch.phys_plt_tbl = NULL;
-+ }
-+ if (me->arch.virt_plt_offset == 0) {
-+ __module_free(me->arch.virt_plt_tbl);
-+ me->arch.virt_plt_tbl = NULL;
-+ }
-+
- return 0;
- }
-
-+void module_arch_freeing_init(struct module *mod)
-+{
-+ if (mod->state == MODULE_STATE_LIVE)
-+ return;
-+
-+ if (mod->arch.phys_plt_tbl) {
-+ __module_free(mod->arch.phys_plt_tbl);
-+ mod->arch.phys_plt_tbl = NULL;
-+ }
-+ if (mod->arch.virt_plt_tbl) {
-+ __module_free(mod->arch.virt_plt_tbl);
-+ mod->arch.virt_plt_tbl = NULL;
-+ }
-+}
-+
- void module_arch_cleanup(struct module *mod)
- {
- spin_lock_irq(&dbe_lock);
+++ /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) {
+++ /dev/null
-From 446daf20b0a6790751459cdde0ff9fc8813e54d1 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Mon, 29 Jul 2024 14:09:16 +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>
----
- 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
- return spi_mem_exec_op(spinand->spimem, &op);
- }
-
-+static int w25n01kv_ooblayout_ecc(struct mtd_info *mtd, int section,
-+ struct mtd_oob_region *region)
-+{
-+ if (section > 3)
-+ return -ERANGE;
-+
-+ region->offset = 64 + (8 * section);
-+ region->length = 7;
-+
-+ return 0;
-+}
-+
- 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
- return 0;
- }
-
-+static const struct mtd_ooblayout_ops w25n01kv_ooblayout = {
-+ .ecc = w25n01kv_ooblayout_ecc,
-+ .free = w25n02kv_ooblayout_free,
-+};
-+
- 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
- &update_cache_variants),
- 0,
- SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
-+ SPINAND_INFO("W25N01KV",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21),
-+ NAND_MEMORG(1, 2048, 96, 64, 1024, 20, 1, 1, 1),
-+ NAND_ECCREQ(4, 512),
-+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+ &write_cache_variants,
-+ &update_cache_variants),
-+ 0,
-+ SPINAND_ECCINFO(&w25n01kv_ooblayout, w25n02kv_ecc_get_status)),
- SPINAND_INFO("W25N02KV",
- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
- NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+++ /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
-@@ -2084,7 +2084,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
-@@ -28,6 +28,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)
- {
-@@ -37,6 +101,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);
-@@ -181,61 +248,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)) &
-@@ -272,6 +354,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;
-@@ -293,7 +388,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;
-@@ -317,18 +411,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)
-@@ -336,6 +470,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
-@@ -470,33 +470,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: Thu, 26 Sep 2024 10:41:30 +0200
-Subject: [PATCH] net: gso: fix tcp fraglist segmentation after pull from
- frag_list
-
-Detect tcp gso fraglist skbs with corrupted geometry (see below) and
-pass these to skb_segment instead of skb_segment_list, as the first
-can segment them correctly.
-
-Valid SKB_GSO_FRAGLIST skbs
-- consist of two or more segments
-- the head_skb holds the protocol headers plus first gso_size
-- one or more frag_list skbs hold exactly one segment
-- all but the last must be gso_size
-
-Optional datapath hooks such as NAT and BPF (bpf_skb_pull_data) can
-modify these skbs, breaking these invariants.
-
-In extreme cases they pull all data into skb linear. For TCP, this
-causes a NULL ptr deref in __tcpv4_gso_segment_list_csum at
-tcp_hdr(seg->next).
-
-Detect invalid geometry due to pull, by checking head_skb size.
-Don't just drop, as this may blackhole a destination. Convert to be
-able to pass to regular skb_segment.
-
-Approach and description based on a patch by Willem de Bruijn.
-
-Link: https://lore.kernel.org/netdev/20240428142913.18666-1-shiming.cheng@mediatek.com/
-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>
----
-
---- a/net/ipv4/tcp_offload.c
-+++ b/net/ipv4/tcp_offload.c
-@@ -101,8 +101,14 @@ 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 (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) {
-+ struct tcphdr *th = tcp_hdr(skb);
-+
-+ if (skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size)
-+ return __tcp4_gso_segment_list(skb, features);
-+
-+ skb->ip_summed = CHECKSUM_NONE;
-+ }
-
- if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
- const struct iphdr *iph = ip_hdr(skb);
---- a/net/ipv6/tcpv6_offload.c
-+++ b/net/ipv6/tcpv6_offload.c
-@@ -158,8 +158,14 @@ 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 (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) {
-+ struct tcphdr *th = tcp_hdr(skb);
-+
-+ if (skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size)
-+ return __tcp6_gso_segment_list(skb, features);
-+
-+ skb->ip_summed = CHECKSUM_NONE;
-+ }
-
- if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
- const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 14 Feb 2024 15:24:41 +0100
-Subject: [PATCH] netfilter: nf_tables: fix bidirectional offload regression
-
-Commit 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules")
-made unidirectional flow offload possible, while completely ignoring (and
-breaking) bidirectional flow offload for nftables.
-Add the missing flag that was left out as an exercise for the reader :)
-
-Cc: Vlad Buslov <vladbu@nvidia.com>
-Fixes: 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -361,6 +361,7 @@ static void nft_flow_offload_eval(const
- ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
- }
-
-+ __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
- ret = flow_offload_add(flowtable, flow);
- if (ret < 0)
- goto err_flow_add;
+++ /dev/null
-From 3b5a603bf66236b956287909556fd7ad4904450c 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
-
-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>
----
- arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi
-+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi
-@@ -275,6 +275,8 @@
- clocks = <&gcc GCC_MDIO_AHB_CLK>;
- clock-names = "gcc_mdio_ahb_clk";
-
-+ clock-frequency = <6250000>;
-+
- status = "disabled";
- };
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 15 Oct 2024 10:13:55 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: fix memory corruption during fq
- dma init
-
-The loop responsible for allocating up to MTK_FQ_DMA_LENGTH buffers must
-only touch as many descriptors, otherwise it ends up corrupting unrelated
-memory. Fix the loop iteration count accordingly.
-
-Fixes: c57e55819443 ("net: ethernet: mtk_eth_soc: handle dma buffer size soc specific")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1172,7 +1172,7 @@ static int mtk_init_fq_dma(struct mtk_et
- if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
- return -ENOMEM;
-
-- for (i = 0; i < cnt; i++) {
-+ for (i = 0; i < len; i++) {
- struct mtk_tx_dma_v2 *txd;
-
- txd = eth->scratch_ring + (j * MTK_FQ_DMA_LENGTH + i) * soc->tx.desc_size;
+++ /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");
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Subject: debloat: disable common USB quirks
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
- drivers/usb/host/pci-quirks.c | 16 ++++++++++++++++
- drivers/usb/host/pci-quirks.h | 18 +++++++++++++++++-
- include/linux/usb/hcd.h | 7 +++++++
- 3 files changed, 40 insertions(+), 1 deletion(-)
-
---- a/drivers/usb/host/pci-quirks.c
-+++ b/drivers/usb/host/pci-quirks.c
-@@ -128,6 +128,8 @@ struct amd_chipset_type {
- u8 rev;
- };
-
-+#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
-+
- static struct amd_chipset_info {
- struct pci_dev *nb_dev;
- struct pci_dev *smbus_dev;
-@@ -631,6 +633,10 @@ bool usb_amd_pt_check_port(struct device
- }
- EXPORT_SYMBOL_GPL(usb_amd_pt_check_port);
-
-+#endif /* CONFIG_PCI_DISABLE_COMMON_QUIRKS */
-+
-+#if IS_ENABLED(CONFIG_USB_UHCI_HCD)
-+
- /*
- * Make sure the controller is completely inactive, unable to
- * generate interrupts or do DMA.
-@@ -710,8 +716,17 @@ reset_needed:
- uhci_reset_hc(pdev, base);
- return 1;
- }
-+#else
-+int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
-+{
-+ return 0;
-+}
-+
-+#endif
- EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
-
-+#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
-+
- static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask)
- {
- u16 cmd;
-@@ -1283,3 +1298,4 @@ static void quirk_usb_early_handoff(stru
- }
- DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
- PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff);
-+#endif
---- a/drivers/usb/host/pci-quirks.h
-+++ b/drivers/usb/host/pci-quirks.h
-@@ -5,6 +5,9 @@
- #ifdef CONFIG_USB_PCI
- void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
- int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
-+#endif /* CONFIG_USB_PCI */
-+
-+#if defined(CONFIG_USB_PCI) && !defined(CONFIG_PCI_DISABLE_COMMON_QUIRKS)
- int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev);
- bool usb_amd_hang_symptom_quirk(void);
- bool usb_amd_prefetch_quirk(void);
-@@ -19,6 +22,18 @@ void sb800_prefetch(struct device *dev,
- bool usb_amd_pt_check_port(struct device *device, int port);
- #else
- struct pci_dev;
-+static inline int usb_amd_quirk_pll_check(void)
-+{
-+ return 0;
-+}
-+static inline bool usb_amd_hang_symptom_quirk(void)
-+{
-+ return false;
-+}
-+static inline bool usb_amd_prefetch_quirk(void)
-+{
-+ return false;
-+}
- static inline void usb_amd_quirk_pll_disable(void) {}
- static inline void usb_amd_quirk_pll_enable(void) {}
- static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {}
-@@ -29,6 +44,11 @@ static inline bool usb_amd_pt_check_port
- {
- return false;
- }
-+static inline void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) {}
-+static inline bool usb_xhci_needs_pci_reset(struct pci_dev *pdev)
-+{
-+ return false;
-+}
- #endif /* CONFIG_USB_PCI */
-
- #endif /* __LINUX_USB_PCI_QUIRKS_H */
---- a/include/linux/usb/hcd.h
-+++ b/include/linux/usb/hcd.h
-@@ -485,7 +485,14 @@ extern int usb_hcd_pci_probe(struct pci_
- extern void usb_hcd_pci_remove(struct pci_dev *dev);
- extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
-
-+#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
- extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev);
-+#else
-+static inline int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev)
-+{
-+ return 0;
-+}
-+#endif
-
- extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
- #endif /* CONFIG_USB_PCI */
+++ /dev/null
-From d9c8bc8c1408f3e8529db6e4e04017b4c579c342 Mon Sep 17 00:00:00 2001
-From: Pawel Dembicki <paweldembicki@gmail.com>
-Date: Sun, 18 Feb 2018 17:08:04 +0100
-Subject: [PATCH] w1: gpio: fix problem with platfom data in w1-gpio
-
-In devices, where fdt is used, is impossible to apply platform data
-without proper fdt node.
-
-This patch allow to use platform data in devices with fdt.
-
-Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
----
- drivers/w1/masters/w1-gpio.c | 7 +++----
- 1 file changed, 3 insertions(+), 4 deletions(-)
-
---- a/drivers/w1/masters/w1-gpio.c
-+++ b/drivers/w1/masters/w1-gpio.c
-@@ -76,7 +76,7 @@ static int w1_gpio_probe(struct platform
- enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
- int err;
-
-- if (of_have_populated_dt()) {
-+ if (of_have_populated_dt() && !dev_get_platdata(&pdev->dev)) {
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
+++ /dev/null
-From 38eb5b3370c29515d2ce92adac2d6eba96f276f5 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
-
-Add LED_FUNCTION_MOBILE for LEDs that indicate status of mobile network
-connection. This is useful to distinguish those LEDs from LEDs that
-indicates status of wired "wan" connection.
-
-example (on stock fw):
-
-IIJ SA-W2 has "Mobile" LEDs that indicate status (no signal, too low,
-low, good) of mobile network connection via dongle connected to USB
-port.
-
-- no signal: (none, turned off)
-- too low: green:mobile & red:mobile (amber, blink)
-- low: green:mobile & red:mobile (amber, turned on)
-- good: green:mobile (turned on)
-
-Suggested-by: Hauke Mehrtens <hauke@hauke-m.de>
-Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
----
- include/dt-bindings/leds/common.h | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/include/dt-bindings/leds/common.h
-+++ b/include/dt-bindings/leds/common.h
-@@ -90,6 +90,7 @@
- #define LED_FUNCTION_INDICATOR "indicator"
- #define LED_FUNCTION_LAN "lan"
- #define LED_FUNCTION_MAIL "mail"
-+#define LED_FUNCTION_MOBILE "mobile"
- #define LED_FUNCTION_MTD "mtd"
- #define LED_FUNCTION_PANIC "panic"
- #define LED_FUNCTION_PROGRAMMING "programming"
+++ /dev/null
-From e22afe910afcfb51b6ba6a0ae776939959727f54 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
-
-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
-distinguish those LEDs from LEDs that indicate link status (up/down).
-
-example:
-
-Fortinet FortiGate 30E/50E have LEDs that indicate link speed on each
-of the ethernet ports in addition to LEDs that indicate link status
-(up/down).
-
-- 1000 Mbps: green:speed-(lan|wan)-N
-- 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>
----
- include/dt-bindings/leds/common.h | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/include/dt-bindings/leds/common.h
-+++ b/include/dt-bindings/leds/common.h
-@@ -96,6 +96,8 @@
- #define LED_FUNCTION_PROGRAMMING "programming"
- #define LED_FUNCTION_RX "rx"
- #define LED_FUNCTION_SD "sd"
-+#define LED_FUNCTION_SPEED_LAN "speed-lan"
-+#define LED_FUNCTION_SPEED_WAN "speed-wan"
- #define LED_FUNCTION_STANDBY "standby"
- #define LED_FUNCTION_TORCH "torch"
- #define LED_FUNCTION_TX "tx"
+++ /dev/null
-From a7a94ca21ac0f347f683d33c72b4aab57ce5eec3 Mon Sep 17 00:00:00 2001
-From: Florian Eckert <fe@dev.tdt.de>
-Date: Mon, 20 Nov 2023 11:13:20 +0100
-Subject: [PATCH] tools/thermal/tmon: Fix compilation warning for wrong format
-
-The following warnings are shown during compilation:
-
-tui.c: In function 'show_cooling_device':
- tui.c:216:40: warning: format '%d' expects argument of type 'int', but
-argument 7 has type 'long unsigned int' [-Wformat=]
- 216 | "%02d %12.12s%6d %6d",
- | ~~^
- | |
- | int
- | %6ld
- ......
- 219 | ptdata.cdi[j].cur_state,
- | ~~~~~~~~~~~~~~~~~~~~~~~
- | |
- | long unsigned int
- tui.c:216:44: warning: format '%d' expects argument of type 'int', but
-argument 8 has type 'long unsigned int' [-Wformat=]
- 216 | "%02d %12.12s%6d %6d",
- | ~~^
- | |
- | int
- | %6ld
- ......
- 220 | ptdata.cdi[j].max_state);
- | ~~~~~~~~~~~~~~~~~~~~~~~
- | |
- | long unsigned int
-
-To fix this, the correct string format must be used for printing.
-
-Signed-off-by: Florian Eckert <fe@dev.tdt.de>
----
- tools/thermal/tmon/tui.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/tools/thermal/tmon/tui.c
-+++ b/tools/thermal/tmon/tui.c
-@@ -213,7 +213,7 @@ void show_cooling_device(void)
- * cooling device instances. skip unused idr.
- */
- mvwprintw(cooling_device_window, j + 2, 1,
-- "%02d %12.12s%6d %6d",
-+ "%02d %12.12s%6lu %6lu",
- ptdata.cdi[j].instance,
- ptdata.cdi[j].type,
- ptdata.cdi[j].cur_state,